1.wait和notify,notifyAll:
- wait和notify,notifyAll是Object類方法,因為等待和喚醒必須是同一個鎖,不可以對不同鎖中的執行緒進行喚醒,而鎖可以是任意物件,所以可以被任意物件呼叫的方法,定義在Object基類中。
- wait()方法:對此物件呼叫wait方法導致本執行緒放棄物件鎖,讓執行緒處於凍結狀態,進入等待執行緒的執行緒池當中。wait是指已經進入同步鎖的執行緒,讓自己暫時讓出同步鎖,以便使其他正在等待此鎖的執行緒可以進入同步鎖並執行,只有其它執行緒呼叫notify方法或者notifyAll方法後,才能喚醒執行緒池中等待的執行緒。
- notify()方法:喚醒執行緒池中的任意一個執行緒。
- notifyAll方法:喚醒執行緒池中的所有執行緒。
2.執行緒間通訊:多個執行緒操作同一個資源,但是操作的動作不同,任務不同。
3.等待喚醒機制:操作共享資料的不同動作,一個存入,一個取出;當輸入一個的時候,另一個要取出。先讓輸入執行緒等待,等輸出執行緒取出後,再喚醒輸入執行緒。
下面看一個例子:需求是要求兩個執行緒操作Resource資源類中的成員變數name,sex,要求一個執行緒往共享資源類中存入一個姓名和一個姓別,即存入一個資源物件,另一個執行緒就取出資源類中的物件。即存入一個,就要取出,資源類中物件為空的時候,就再存入一個。這個例子可以用等待喚醒機制解決。
思路:這裡線上程程式碼裡面設計一個姓名為peter,性別為man;另一個姓名為“李明”,性別為“男”。通過模2進行切換。輸入執行緒選擇操作這連個物件。
class Res{ String name; String sex; boolean flag = false; } class Input implements Runnable{ Object obj = new Object(); private Res r ; public Input(Res r){//通過構造方法初始化資源,把Res傳遞進來,一構造物件,資源被初始化 this.r = r; } public void run(){ int x= 0; while(true){ synchronized(r){//同步程式碼塊,鎖為r if(r.flag){//如果資源裡面有一個姓名和名字了,input執行緒就凍結,釋放執行權,釋放執行資格,釋放鎖 try { r.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(x == 0){//切換x,通過模2進行切換 r.name="peter"; r.sex="man"; }else{ r.name="李明"; r.sex="男男男男"; } x = (x+1)%2;//取模運算,模2=0,取名字為Mike,性別為man,不等於0,則取小紅,女女女女 r.flag = true;//當存入一個,標誌變為true,輸入執行緒進入等待,處於凍結狀態,等待輸出執行緒輸出 r.notify();//喚醒執行緒池中的其它執行緒,喚醒第一個等待的輸出執行緒 } } } } class OutPut implements Runnable{ Object obj = new Object(); private Res r; public OutPut(Res r){ this.r = r; } public void run() { while(true){ synchronized(r){ if(!r.flag) try { r.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //如果r.flag=true,說明資源Res裡面有一個東西,輸出裡面名字和性別 System.out.println(r.name+ "......"+r.sex); r.flag=false;//當輸出一個,標誌改為false,這時輸出執行緒進入執行緒池進行等待,直到被喚醒 r.notify();//喚醒輸入執行緒,導致當前執行緒等待 } } } } public class ThreadorThreadSpeak { /** * @param args */ public static void main(String[] args) { //建立資源 Res r = new Res();//比喻為煤資源 //建立任務 Input in = new Input(r); OutPut out = new OutPut(r); //建立執行緒 Thread t1 = new Thread(in);//輸入執行緒 Thread t2 = new Thread(out);//輸出執行緒 //開啟執行緒 t1.start(); t2.start(); } }
優化上面的程式碼,把同步程式碼塊中的程式碼封裝成同步函式
class Resource { private String name; private String sex; private boolean flag = false; public synchronized void set(String name,String sex) { if(flag) try{this.wait();}catch(InterruptedException e){} this.name = name; this.sex = sex; flag = true; this.notify(); } public synchronized void out() { if(!flag) try{this.wait();}catch(InterruptedException e){} System.out.println(name+"...+...."+sex); flag = false; notify(); } } //輸入 class Input implements Runnable { Resource r ; Input(Resource r) { this.r = r; } public void run() { int x = 0; while(true) {
//取2模進行切換 if(x==0) { r.set("peter","nan"); } else { r.set("李明","男男男男"); } x = (x+1)%2; } } } //輸出 class Output implements Runnable { Resource r; Output(Resource r) { this.r = r; } public void run() { while(true) { r.out(); } } } class ResourceDemo3 { public static void main(String[] args) { //建立資源。 Resource r = new Resource(); //建立任務。 Input in = new Input(r); Output out = new Output(r); //建立執行緒,執行路徑。 Thread t1 = new Thread(in); Thread t2 = new Thread(out); //開啟執行緒 t1.start(); t2.start(); } }
輸出:
peter......man
李明......男男男男
peter......man
李明......男男男男
peter......man