------- <a href="http://www.itheima.com" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="blank">android教育訓練</a>、<a href="http://www.itheima.com" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="blank">java教育訓練</a>、期待與您交流! ----------
1.異常處理機制:
(1)概念:就是程式在運作時出現的不正常情況。
(2)error與Exception的差別:
Error:對于嚴重的問題,Java通過Error類進行描述。一般不編寫針對性的代碼
對其進行處理。
Exception:對于非嚴重的問題,Java通過Exception來進行描述,可以使用針對
性的方法去處理。
結論:無論Error還是Exception都有一些共性的内容比如:不正常的内容,引發原因,有共同的父類Throwable。
(3)異常的處理:
Java提供了特有的語句進行處理
try
{
需要檢測的代碼;
}
catch(異常類 變量)
{
處理異常的代碼;
}
finally
{
一定會執行的語句;
}
(4)對捕獲到的異常處理的方法:
String getMessage(); 擷取異常資訊。
String toString(); 異常名稱,異常資訊,以字元串形式傳回。
printStackTrace()異常名稱,異常資訊,異常出現的位置 jvm預設的異常處理機制。
1、聲明異常時,建議聲明更為具體的異常,這樣處理的更為具體。
2、對方聲明幾個異常,就對應幾個catch代碼塊如果多個異常的出現繼承關系,父類異常catch放在最下面自定義異
常。
(5)自定義異常:
1、項目中會出現特有的問題,而這些問題别沒有被java所描述并封裝成對象
2、自定義異常必須手動抛出 用throw關鍵字。
3、當在函數内部出現了throw抛出異常,必須給出相應的處理動作要麼在函
數内部try catch 要麼在函數上聲明讓調用者處理 RuntimeException除外。
4、一般,函數内部出現異常,要在函數上聲明。
5、自定義異常必須是自定義類繼承Exception,因為異常體系有一個特點,異
常類和異常對象都具有可抛性,而可抛性是throwable體系特有的特點。
6、那麼該如何定義異常資訊呢?因為父類已經把異常資訊的操作都完成了,
是以子類在構造函數時,通過super語句将資訊傳遞給父類,就可以直接
通過getMessage獲得自定義的異常資訊。
代碼例子:
需求:在這個運作程式中,除數不能是負數。
代碼例子:
class FuShuException extends Exception
{
FuShuException(String msg)
{
super(msg);
}
}
class Demo
{
int div(int a,int b)throws FuShuException
{
if(b<0)
throw new FuShuException("出現了商為負數的情況-------by FuSu");
return a/b;
}
}
class ExceptionDemo
{
public static void main(String[] args)
{
Demo d =new Demo();
try
{
int x=d.div(4,-1);
System.out.println(x);
}
catch (FuShuException e)
{
System.out.println(e.toString());
}
}
}
(6)throws與throw的差別:
throws用在函數上
throw用在函數中
throws後面跟的是異常類,可以跟多個,用逗号分開
throw後面跟的是異常對象,。
(7)運作時異常RuntimeException
1、在函數内出現了這個異常,函數上可以不去聲明,編譯一樣可以通過,如果在函數上聲明了這個異常,調用者
可以不用處理,編譯也可以通過。
2、運作時異常之是以可以不用在函數上聲明,是因為不需要讓調用者處理,當該異常發生,希望程式停止,對代
碼進行修改。
3、在自定義異常時,如果這個異常的發生,無法再進行運算,可以讓着個異常繼承運作時異常。
4、異常分為兩種:
4.1、編譯時被檢測的異常,在函數上标示出,讓調用者去處理,問題可以處理,不需要修改代碼。
4.2、編譯時不被檢測的異常(運作時異常),必須停下程式,修改代碼。
(8)finally語句:
一定要執行的語句,比如資料庫操作:斷開連接配接 ,通常用于關閉資源。
(9)異常處理的格式:
1、try{} catch(){}
2、try{} catch(){} finally{}
3、try{} finally{}
代碼例子:
try{} catch(){} 語句:
class Demo
{
//不需要聲明因為内部已經解決
public void method()
{
try
{
thow new Exception();
}
catch(Excetion e)
{
}
}
}
try{} finally{} 語句:
class Demo
{
public void method()
{
try
{
thow new Exception();
}
finally
{
}
}
}
沒有catch,代表異常沒有被處理,如果這個異常是編譯時異常,那麼這個異常必須聲明。
注意:try catch中的return語句要先檢查finally是否在,然後才能傳回。
(10)異常在子父類中的展現:
1、子類覆寫父類異常 時,如果父類抛出了異常,那麼子類的覆寫方法,隻能抛出父類的異常或者父類異常的子
類。
2、如果父類抛出多個異常,那麼子類隻能抛出父類抛出異常的子集。
3、如果父類或者接口沒有抛出異常,那麼子類在覆寫方法時也不能抛出異常如果子類發生了異常,隻能try 不能
抛出。
2.String:
(1)String特點:
1、字元串是一個特殊的對象。
2、字元串一旦初始化就不可以被改變。
3、String不能被繼承。
4、String str = “abc”與 String str1 = new String(“abc”);的差別:
“abc”是一個對象,前者隻建立了一個對象,後者為兩個對象。
(2)String裡面的常用方法:
1、擷取的方法:
//字元串中的包含的字元數,也就是字元串的長度。
int length();
//根據指定位置擷取位置上的某個字元。
char charAt(int index);
//傳回的是ch第一次在字元串中出現的位置。
int indexOf(int ch);
//傳回的是從fromIndex指定位置開始,查找 ch在在字元串中出現的位置。
int indexOf(int ch,int fromIndex);
//傳回指定子字元串在此字元串中第一次出現處的索引。
int indexOf(String str);
//從fromIndex指定位置開始,擷取子字元串在字元串中出現的位置。
int indexOf(String str,int fromIndex);
//傳回的是指定子字元串在此字元串中最右邊出現的位置。
int lastIndexOf(String str);
//從指定位置開始擷取該字元串中的一部分,包含結尾。
String substring(int index);
String substring(int index,int fromIndex);
2、判斷
boolean contains(str); //字元串中是否包含某一個子串。
boolean isEmpty(); //字元串中是否有内容。
boolean startsWith(str); //字元串是否是以指定内容開頭。
boolean endsWith(str); //字元串是否是以指定内容結尾。
boolean equalsIgnoreCase(); //判斷内容是否相同,并忽略大小寫。
boolean equals("String str") //判斷字元串内容是否相同
3、轉換
将字元數組轉換成字元串:
構造函數:
String(char[]), // 将整個字元數組轉成字元串
String(char[],offset,count); // 将數組中的一部分轉成字元串
靜态方法:
static String copyValueOf(char[]); //将整個字元數組轉成字元串。
static String copyValueOf(char[],offset,count);//将數組中的一部分轉成字元串
static String valueOf(char[]);
将字元串轉換成字元數組:
char[] toCharArray();
将位元組數組轉換成字元串:
byte[] getBytes(); 将字元串轉換成位元組數組。
4、替換:
replace(oldchar,newchar);
傳回的是一個新的字元串。如果要替換的字元不存在,傳回的還是原字元串。
replace(str,restr);
5、切割
String[] split(regex);
6、子串
String substring(begin);
String substring(begin,end);
7、轉換大小寫,去除空格,比較
7.1、将字元串轉成大寫或小寫。
String toUpperCase();
String toLowerCase();
7.2、将字元串兩端的多個空格去除。
String trim();
7.3、對兩個字元串進行自然順序的比較。
int compareTo(String);
(3)練習1
模拟一個trim方法,去除字元串兩端的空格。
思路:
1、判斷字元串第一個位置是否是空格,如果是,就繼續向後判斷,直到不
是空格為止,結尾處判斷空格也是一樣。
2、當開始和結尾都判斷到不是空格時,就是要擷取的字元串。
3、必須保證開始的角标小于等于結尾的角标,否則沒有意義。
代碼例子:
public static void main(String[] args)
{
String s1 =" zhang hao ";
int start=0,end =s1.length()-1;
while(start<=end && s1.charAt(start)==' ')
start++;
while(start<=end && s1.charAt(end)==' ')
end--;
//因為包含頭不包含尾,是以結 尾處需要加1。
System.out.println(s1.substring(start,end+1));
}
(4)練習2
将一個字元串進行整體反轉。
思路:
1、将字元串變成字元數組
2、将數組進行反轉
3、将數組變成字元串
4、隻要将反轉的部分的開始和結束位置作為參數傳遞即可。
代碼例子:
class StringDemo1
{
public static void main(String[] args)
{
String s = "zhanghao haha";
System.out.println(reverseString(s));
}
public static String reverseString(String s)
{
char[] arr =s.toCharArray(); //字元串變字元數組
reverse(arr); //将數組進行反轉
return new String(arr); //将數組變成字元串
}
private static void reverse(char[] arr)
{
for(int start=0, end=arr.length-1; start<end; start++,end--)
{
swap(arr,start,end);
}
private static void swap(char[] arr,int x,int y)
{
char temp =arr[x];
arr[x]=arr[y];
arr[y]=temp;
}
}
将字元串中的指定部分進行反轉。
代碼例子:
class StringDemo2
{
public static void main(String[] args)
{
String s = "zhanghao haha";
System.out.println(reverseString(s,2,7));
}
public static String reverseString(String s,int start,int end)
{
char[] arr =s.toCharArray();
reverse(arr,start,end);
return new String(arr);
//System.out.println(new String(arr));
}
private static void reverse(char[] arr,int x,int y)
{
for(int start=x, end=y-1; start<end; start++,end--)
swap(arr,start,end);
}
private static void swap(char[] arr,int x,int y)
{
char temp =arr[x];
arr[x]=arr[y];
arr[y]=temp;
}
}
(5)練習3:
擷取一個字元串在另一個字元串中出現的次數。
“abkkcdkkefkkskk”
思路:
1、定義一個計數器。
2、擷取KK第一次出現的位置。
3、從第一次出現的位置後,剩餘的字元串中繼續擷取kk出現的位置,每擷取一就計數一次。
4、當擷取不到時,計數完成。
代碼例子:
//第一種方式,比較繁瑣,用的是indexOf(String x)和substring()。
class StringDemo1
{
public static void main(String[] args)
{
String str ="abkkcdkkefkkskk";
System.out.println(getSubCount_2(str,"kk"));
}
public static int getSubCount_2(String str,String key)
{
int count =0;
int index =0;
while((index=str.indexOf(key))!=-1)
{
str=str.substring(index+key.length());
count++;
}
return count;
}
}
//第二種方式,比較簡單,用的是indexOf(String x,int y);
class StringDemo2
{
public static void main(String[] args)
{
String str ="abkkcdkkefkkskk";
System.out.println(getSubCount(str,"kk"));
}
public static int getSubCount(String str,String key)
{
int count =0;
int index =0;
while((index=str.indexOf(key,index))!=-1)
System.out.println("index"+index);
index=index+key.length();
count++;
}
return count;
}
(6)練習4:
擷取倆個字元串中最大相同的子串。
思路:
1、将短的那個字串按照長度遞減的方式擷取到。
2、将每次擷取到的子串去長度中判斷是否包含,如果包含,說明已經找到了。
代碼例子:
class StringDemo2
{
public static void main(String[] args)
{
String s1 ="abcwerthelloyuiodef";
String s2 ="cvhellobnm";
System.out.println(getSubString(s1,s2));
}
public static String getSubString(String s1,String s2)
{
for(int x =0; x<s2.length(); x++)
{
for(int y=0,z=s2.length()-x; z!= s2.length()+1; y++,z++)
{
String temp = s2.substring(y,z);
if(s1.contains(temp))
return temp;
}
}
return "";
}
}
3.StingBuffer
(1)StingBuffer的特點及作用:
字元串的組成原理就是通過該類實作的。
StingBuffer這個類被final修飾,不能被繼承。
StingBuffer是線程同步的。
StingBuffer可以對字元串内容進行增删。
StingBuffer是一個容器。
很多方法與StingBuffer相同。
StingBuffer是可變長度的。
可以直接操作多個資料類型。
一次最終會通過toString方法變成字元串。
(2)StingBuffer裡面的常用方法:
1、存儲:StingBuffer append();
2、删除:StingBuffer delete(int start,int end);
StingBuffer deleteCharAt(int index);
3、擷取:char charAt(int index);
int indexOf(String str);
int lastIndexOf(String str);
4、修改:StingBuffer replace(int start, int end, String str);
5、反轉:StingBuffer reverse();
(3)什麼時候使用緩沖區:
當資料類型不确定,資料的個數不确定,而且最後變成字元串的時候。
4.StingBuilder
(1)StringBuilder特點與作用:
一個可變的字元序列,此類提供一個與StingBuffer相容的API,但不保證同步。
該類被設計用作StingBuffer類的一個簡易的替換,用在字元串緩沖區被單個線程使用個時候,它比StingBuffer的效率要快。
(2)StingBuffer與StringBuilder的差別:
StingBuffer是線程同步的,安全的,需要判斷鎖,效率慢。
StringBuilder是線程不同步的,不安全,不需要判斷鎖,效率快。
5.正規表達式:
(1)概念:符合一定規則的表達式。
1、特點:用一些特定的符号來表示一些代碼的操作。
2、作用:專門用于操作字元串。
3、好處:可以簡化對字元串的複雜操作。
4、弊端:符号定義越多,正則越長,閱讀性越差。
(2)正規表達式的構造摘要:
常用的:
1、字元類
[abc] a、b 或 c(簡單類)
[^abc] 任何字元,除了 a、b 或 c(否定)
[a-zA-Z] a 到 z 或 A 到 Z,兩頭的字母包括在内(範圍)
[a-d[m-p]] a 到 d 或 m 到 p:[a-dm-p](并集)
[a-z&&[def]] d、e 或 f(交集)
[a-z&&[^bc]] a 到 z,除了 b 和 c:[ad-z](減去)
[a-z&&[^m-p]] a 到 z,而非 m 到 p:[a-lq-z](減去)
2、預定義字元類:
. 任何字元(與行結束符可能比對也可能不比對)
\d 數字:[0-9]
\D 非數字: [^0-9]
\s 空白字元:[ \t\n\x0B\f\r]
\S 非空白字元:[^\s]
\w 單詞字元:[a-zA-Z_0-9]
\W 非單詞字元:[^\w]
3、邊界比對器 :
^ 行的開頭
$ 行的結尾
\b 單詞邊界
\B 非單詞邊界
\A 輸入的開頭
\G 上一個比對的結尾
\Z 輸入的結尾,僅用于最後的結束符(如果有的話)
\z 輸入的結尾
4、Greedy 數量詞:
X? X,一次或一次也沒有
X* X,零次或多次
X+ X,一次或多次
X{n} X,恰好 n 次
X{n,} X,至少 n 次
X{n,m} X,至少 n 次,但是不超過 m 次
(3)具體的操作功能:
1、比對: 用的是String中的matches方法。
用規則比對整個字元串,隻有有一處不符合規則,就比對結束,
傳回false,傳回的是真假。
2、切割:用的是String 中的split方法。
傳回的是規則以外的字元串數組。
3、替換:用的是String中的replaceAll方法。
傳回的是一個替換後的字元串,replaceAll裡面需要傳兩個參數,
一個替換的規則,一個就是要替換的字元串。
4、擷取:将字元串中符合規則的子串取出。
步驟:
1、将正規表達式封裝成對象。
2、讓正則對象和要操作的字元串想關聯。
3、關聯後擷取正則比對引擎。
4、通過引擎對符合規則的字元串進行操作,比如取出。
代碼例子:
import java.util.regex.*;
class RegexDemo
{
public static void main(String[] args)
{
getDemo();
}
public static void getDemo()
{
String str = "ming tian jiu yao fang jia le ,da jia。";
System.out.println(str);
String reg = "\\b[a-z]{4}\\b";
//将規則封裝成對象。
Pattern p = Pattern.compile(reg);
//讓正則對象和要作用的字元串相關聯。擷取比對器對象。
Matcher m = p.matcher(str);
//System.out.println(m.matches());
//System.out.println("matches:"+m.matches());
while(m.find())
{
System.out.println(m.group());
System.out.println(m.start()+"...."+m.end());
}
}
}
6.多線程:
(1)線程和程序概念:
1、程序:正在進行中的程式。
2、線程:程序中的多條執行路徑。
(2)程序與線程的關系:
1、線程控制着程序的執行進度。
2、一個程序中至少有一個線程,就是主函數。
(3)多線程的特點:
随機性,每個線程都擷取CPU的執行權,到底誰執行,執行多久由CPU來決定。多個線程之間交 替執行。
(4)啟動線程:
啟動線程的唯一方法是:線程類對象.start();
用于開啟線程并執行線程中的run();
線程類的run()方法,用于封裝要運作的代碼。
(5)建立的線程的兩種方式:
1、定義線程的第一種方法:
步驟:
1.1、定義一個類繼承Thread類。
1.2、複寫Thread類中的run方法,用于将自定義代碼存儲到run方法中,
讓線程運作。
1.3、調用線程的start方法來啟動線程。
代碼例子:
class Demo1 extends Thread
{
//覆寫run方法。
public void run()
{
System.out.println("demo1 run");
}
}
class ThreadDemo
{
public static void main(String[] args)
{
//定義子類對象。
Demo1 d=new Demo1();
//啟動線程。
t.start();
}
}
2、定義線程的第二種方法:
2.1、定義一個類實作Runnable接口。
2.2、覆寫Runnable中的run方法。
2.3、通過Thread類建立線程對象。
2.4、将Runnable接口的子類對象作為實際參數傳遞到Thread類的構造函數中。
2.5、調用Thread類的start方法啟動線程,調用Runnable接口的子類的run方
法。
代碼例子:
class Demo2 implements Runnable
{
//覆寫Runnable中的run方法
public void run()
{
System.out.println("demo2 run");
}
}
class RunnableDemo
{
public static void main(String[] args)
{
//定義一個Runnable接口的子類對象。
Demo2 d=new Demo2();
Thread t = new Thread(d);
//調用Thread類中的start方法啟動線程,并調用Runnable接口中的run
方法。*/
t.start();
}
}
3、線程的兩種建立方式有什麼差別嗎?
3.1、繼承Thread類,線程代碼存方法在Thread類的run方法中。
實作Runnable接口,線程代碼存方法在Runnable接口的run方法中。
3.2、實作方式的好處:
避免了單繼承的局限性,在定義線程時,使用實作方式能夠對外提供
功能,而且還能被多線程操作。
代碼例子:
售票:
需求:簡單的賣票程式,多個視窗同時買票。
class Ticket implements Runnable
{
//定義100張票。
private int ticket=100;
public void run()
{
while(true)
{
if(ticket>0)
{
//擷取線程的名稱,以及各個線程買票的情況。
System.out.println(Thread.currentThread().getName()+"..."+ticket--);
}
}
}
}
class TicketDemo
{
public static void main(String[] args)
{
Ticket tic=new Ticket();
//建立4個線程。
Thread t1 = new Thread(tic);
Thread t2 = new Thread(tic);
Thread t3 = new Thread(tic);
Thread t4 = new Thread(tic);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
(6)多線程安全問題:
問題出現的狀況:
當多個線程在操作同一個共享資料時,一個線程的多條語句中隻執行了一
部分,還沒有執行完,另一條線程就進來執行了,導緻共享資料的錯誤。
解決辦法:
對于有多條操作共享資料的語句,隻能讓一個線程全部執行完,在執行的過程中,其它的線程不能參與執行。
Java對于多線程的安全問題提供了專業的解決方法。
1、同步代碼塊來實作安全:
synchronized(對象)
{
需要被同步的語句;
}
1.1、那麼那些資料需要被同步呢?
看那些語句在操作共享資料,就同步這些語句。
1.2、裡面存放的對象如同鎖,沒有鎖的線程就算取得了CPU的執行權也不能執行,線程加鎖帶來的弊端:要有鎖對象,是以耗資源,要判斷鎖,是以效率稍減。
2、同步函數
public synchronized void method(Type args)
{
需要被同步的語句;
}
2.1、同步非靜态函數用的鎖是this。
2.2、如果同步靜态函數:所用的鎖不是this,因為靜态方法中不能出現this,
用的是 類名.class是Class類型對象。
3、如果一個程式中有安全問題,使用同步時應注意:
3.1、明确多線程運作代碼(一般為run方法裡調用的語句,以及其附帶語句(調
用了其它的方法)有哪些
3.2、明确共享資料。
3、明确多線程運作代碼中哪些語句是操作共享資料的。
4、同步的前提:
1、必須有兩個或兩個以上的線程。
2、必須是多個線程使用同一把鎖。
解釋:也就是保證同步中隻能有一個線程在運作。
(7)單例設計模式
1、單例設計模式的作用:
解決一個類在記憶體中隻用一個對象。
2、 餓漢式步驟:
2.1、将構造函數私有化。
2.2、在類中建立一個私有并靜态的本類對象。
2.3、對外提供一個方法可以擷取到該對象。
代碼例子:
class Single1
{
//将構造函數私有化。
private Single1(){}
//在類中建立一個私有并靜态的本類對象。
private static Single1 s1 =new Single1();
//對外提供一個方法可以擷取到該對象。
public static Single1 getInstance()
{
return s1;
}
}
class Single1Demo
{
public static void main(String[] args)
{
//用類名調用擷取對象的方法。
Single1 ss1 =Single1.getInstance();
}
}
3、懶漢式步驟:
3.1、私有并靜态一個對象的引用。
3.2、将構造函數私有化。
3.3、對外提供一個方法,加入同步代碼塊,當方法被調用時才會初始化。
代碼例子:
class Single2
{
private static Single2 s2 = null;
private Single2 (){}
public static Single2 getInstance()
{
if(s==null)
{
synchronized(Single2.class)
{
if(s2==null)
{
s2 = new Single();
}
}
}
return s2;
}
public static void main(String[] args)
{
Single2 ss2 = Single2.getInstance();
}
}
解釋:
當a線程進來的時候,判斷s2==null,滿足條件,就擷取了鎖,但是它擷取鎖以後,執行權被b線程搶走了,它就停在這了,擷取執行權的b線程進來判斷s2==null,滿足條件,往下執行的時候發現已經上鎖了,是以它沒有進去同步代碼塊就停下了,并且釋放了執行權這時a線程擷取了執行權,繼續往下執行,判斷為空,就建立了一個對象,并且釋放了鎖,于是b線程又獲得了執行權,它進來同步一看,s2不等于空了,于是它就釋放了執行權,這時又有一條線程c進來了,它判斷s2不等于空,就不用再判斷鎖了。
4、餓漢式和懶漢式有什麼差別?
4.1、懶漢式特點:執行個體的延時加載。
4.2、懶漢式在多線程通路時存在安全問題:
擷取執行個體的方法中有多條語句操作共享資源,是以用同步代碼塊和同步函數都能解決此問題,但效率稍低;可以用雙重判斷來減少判斷鎖的次數。
4.3、懶漢式加同步時使用的鎖是哪個?
這個鎖是該類所屬的位元組碼檔案對象。
(8)死鎖:
1、死鎖的産生:同步中嵌套同步,而使用的鎖不一樣。
代碼例子:
class DeadLock
{
public static void main(String[] args)
{
Test a = new Test(true);
Test b = new Test(false);
Thread t1 = new Thread(a);
Thread t2 = new Thread(b);
t1.start();
t2.start();
}
}
class Test implements Runnable
{
private boolean flag;
Test(boolean flag)
{
this.flag = flag;
}
public void run()
{
if(flag)
{
synchronized(MyLock.locka)
{
System.out.println("if...locka");
synchronized(MyLock.lockb)
{
System.out.println("if...lockb");
}
}
}
else
{
synchronized(MyLock.lockb)
{
System.out.println("else...lockb");
synchronized(MyLock.locka)
{
System.out.println("else...locka");
}
}
}
}
}
class MyLock
{
static Object locka = new Object();
static Object lockb = new Object();
}
結果是:一個拿着a鎖不放,一個拿着b鎖不放,拿着a鎖的進不了b鎖,
拿着b鎖的進不了a鎖。
(9)多線程通信
多線程通信:即多個線程操作同一個資源,但操作的動作不同。
1、等待喚醒機制:
原理:輸入線程如果擷取到了CPU的執行權,它存了一個 zhangsan,man,它存的
時候别的線程進不來,它存完出了同步以後,輸入線程和輸出線程都有可
能搶到CPU的執行權,是以說輸入線程可能有搶到了執行權,是以它又把
“麗麗”,“女女女女”存進去了,然後不斷的搶,不斷的存,不段的覆寫
到某一時刻時,輸出線程搶到了執行權,然後開始列印,但是它不可能隻
搶到一次,是以就不斷的列印同一個名字和性别,是以列印的時候是一大
片相同的,而不是間隔開來的。但是我們想要的是輸入一個就跟着輸出一
個,是以就用到了等待喚醒機制,那麼就是定義一個标記,當輸入線程判
裡面沒有名字時,也就是為false,然後就往裡面存一個名字,存完以後輸
入線程會把false改為true,此時輸入線程還持有執行權,是以它進來以判
斷标記為true就不存了,然後它就等待了,等待了也就是當機了,也就是
放棄了執行資格,然後輸出線程就獲得了執行權,那麼它進去一判斷标記
為true,說明有資料,然後就列印一次,列印完以後吧标記改為false,此
輸出線程仍持有執行權,當它再去判斷的時候發現标記為false,是以就等
待了,也就是放棄了執行資格,等待之前它把輸入線程喚醒了,然後輸入
線程繼續存資料。
代碼例子:
public class ThreadCommunication
{
public static void main(String[] args)
{
Res r = new Res();
new Thread(new Input(r)).start();
new Thread(new Output(r)).start();
}
}
//共享資源
class Res
{
private String name;
private String sex;
private boolean flag = false;
//設定方法
public synchronized void set(String name, String sex)
{
//flag=true表示設定過的還未列印,有資料則等待
if(flag)
try
{
this.wait();
}
catch(InterruptedException e)
{
e.printStackTrace();
}
this.name = name;
this.sex = sex;
flag = true;
this.notify();
}
//列印方法
public synchronized void out()
{
if(!flag)
try
{
this.wait();
}
catch(InterruptedException e)
{
e.printStackTrace();
}
System.out.println(name + ".........." + sex);
flag = false;
this.notify();
}
}
//輸入線程
class Input implements Runnable
{
private Res r;
Input(Res r)
{
this.r = r;
}
public void run()
{
int x = 0;
while(true)
{
if(x==0)
{
r.set("zhangsan", "man");
}
else
{
r.set("麗麗", "女女女女");
}
x = (x+1)%2;//控制x在0和1之間不斷交替,進而讓設定的内容不同。
}
}
}
//輸出線程
class Output implements Runnable
{
private Res r;
Output(Res r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.out();
}
}
}
2、總結:
2.1、線上程池裡等待的線程,往往先喚醒的是第一個。
wait()、notify()、notifyAll()。目前線程必須擁有此對象螢幕(即鎖),鎖隻
有在同步中才有。
2.2、那麼上述方法為何被定義在Object類中?
因為上述方法由鎖調用,鎖可以是任意對象,是以定義在任意對象的父類
Object中。
2.3、wait()和sleep()有何差別:
wait()釋放資源,釋放鎖。
sleep()釋放資源,不釋放鎖。
等待和喚醒的必須是同一個鎖。而鎖可以是任意對象,是以定義在Object
中。
(10)Lock接口和Condition接口
1、JDK1.5中提供了替代同步中隐式鎖的synchronized為顯式鎖方式Lock接口和
Condition接口将Object中的wait(),notify(),notifyAll()替換成了Condition對象
的avait(),signal(),signalAll()方法,Condition對象可以由Lock擷取,實作了本方隻
會喚醒對方的操作。
2、生産者消費者問題替代方案:
1.5版本時,提供了顯式的鎖機制,以及顯式的鎖對象上的等待喚醒操作機制
一個Lock鎖,對應了多個Condition。
代碼例子:
public class ProducerConsumerJDK5
{
public static void main(String[] args)
{
Resource res = new Resource();
Producer p1 = new Producer(res);
Consumer c1 = new Consumer(res);
Thread t1 = new Thread(p1);
Thread t2 = new Thread(p1);
Thread t3 = new Thread(c1);
Thread t4 = new Thread(c1);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Resource
{
private String name;
private int count =1;
boolean flag = false;
Lock lock = new ReentrantLock();
Condition condition_pro = lock.newCondition();
Condition condition_con = lock.newCondition();
public void set(String name) throws Exception
{
lock.lock();
try
{
/*用while循環判斷生産标記,讓被喚醒的線程再次判斷标記,確定
不會出現生産一個而消費兩個的情況*/
while(flag)
condition_pro.await();
this.name = name + "..." + count++;
//讓生産進度變慢,消費進度也随之變慢
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() +" 生産 "+
this.name);
//生産後将生産标記設為true,意為已經生産一個了,快列印。
flag = true;
condition_con.signal();//激活對方線程。
}
finally
{
lock.unlock();//釋放鎖的動作一定要執行
}
}
public void out() throws Exception
{
lock.lock();
try
{
while(!flag)
condition_con.await();
System.out.println(Thread.currentThread().getName() +" 消費 "+ this.name);
flag = false;
condition_pro.signal();
}
finally
{
lock.unlock();
}
}
}
class Producer implements Runnable
{
private Resource res;
public Producer(Resource res)
{
this.res = res;
}
public void run()
{
//不停的生産
while(true)
{
try
{
res.set("包子");
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
}
class Consumer implements Runnable
{
private Resource res;
public Consumer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
try
{
res.out();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
}
3、停止線程:
3.1、直接結束run()方法。
原理:開啟多線程運作,運作代碼通常是循環結構,隻要控制住循環,就可
以讓run方法結束。
特殊情況:當線程處于當機狀态的時候,就不會讀取到标記,那麼這時線程
就不會結束。當機狀态不是停止線程,是将線程挂起。
3.2、使用interrupt()方法。
強制清除其當機狀态,然後讓其恢複到運作狀态,然後就能讀标記了。
4、守護線程:
4.1、setDaemon(true):設定線程為守護線程(也可将其稱為背景線程)。
當正在運作的線程都是守護線程時,jvm即退出,該方法必須在啟動線程前調用。
4.2、前台線程和背景線程:在執行過程中沒有差別(同樣搶占cpu的執行權),
隻在結束時有差別,守護線程依賴于主線程(前台線程),當主線程結束後,
所有守護線程自動結束。
5、等待該現場終止:
方法join(),臨時加入線程,當A線程執行到了B線程的B.join()方法,A線程就等待,直到B線程結束後,A線程才從當機狀态回到運作狀态。
一般使用方式:當滿足一定的條件時,讓某一線程加入進來。
6、優先級:
所有的線程預設的優先級都是5,線程最高優先級是10,線程最低優先級是1。
這裡的main是線程組名(Thread-1由main開啟),線程預設的優先級是5,ThreadGroup,可以讓程式員自己建立線程組。(幾乎用不到)
setPriority(Thread.MAX_PRIORITY)設定優先級最高
Thread.yield():暫停目前正在執行的線程對象,并執行其他線程。
如果有兩個線程操作同一段代碼,代碼中加入Thread.yield(),運作的效果類似是a線程執行一次b線程執行一次,均勻交替的執行。
------- <a href="http://www.itheima.com" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="blank">android教育訓練</a>、<a href="http://www.itheima.com" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="blank">java教育訓練</a>、期待與您交流! ----------