Java多執行緒-52-三個和三個以上執行緒之間的通訊
阿新 • • 發佈:2019-01-13
前面一篇介紹了兩個執行緒之間的通訊,那麼三個執行緒和三個以上執行緒之間的通訊是如何實現呢。我們前面一篇在查詢Object類的時候,知道有一個wait()和notify()方法,同時還有一個notifyAll()方法。這個notfiyAll()方法就是來解決三個以上執行緒通訊的。
1.基於前面知識,新增一個執行緒,看看執行效果
package thread; public class NotifyAll_Demo { public static void main(String[] args) { Printer2 p = new Printer2(); new Thread() { public void run() { while(true) { try { p.print1(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); new Thread() { public void run() { while(true) { try { p.print2(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); new Thread() { public void run() { while(true) { try { p.print3(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } } class Printer2 { private int flag = 1; public void print1() throws InterruptedException { synchronized (this) { if(flag != 1) { this.wait(); // 設定執行緒等待,如果flag 不等於1 } System.out.print("跟"); System.out.print("我"); System.out.print("一"); System.out.print("起"); System.out.println("念"); flag = 2; this.notify(); // 設定flag等於2,使用執行緒喚醒功能,其他執行緒就可以啟動 } } public void print2() throws InterruptedException { synchronized (this) { if(flag != 2) { this.wait(); // 設定執行緒等待,如果flag 不等於2 } System.out.print("做"); System.out.print("測"); System.out.print("試"); System.out.print("死"); System.out.print("路"); System.out.print("一"); System.out.println("條"); flag = 3; this.notify(); //隨機喚醒單個等待的執行緒 } } public void print3() throws InterruptedException { synchronized (this) { if(flag != 3) { this.wait(); // 設定執行緒等待,如果flag 不等於2 } System.out.print("信"); System.out.print("才"); System.out.print("怪"); System.out.println("呢"); flag = 1; this.notify(); //隨機喚醒單個等待的執行緒 } } }
執行效果:
做測試死路一條
跟我一起念
信才怪呢
做測試死路一條
跟我一起念
信才怪呢
做測試死路一條
跟我一起念
信才怪呢
做測試死路一條
跟我一起念
信才怪呢
做測試死路一條
跟我一起念
發現列印順序有問題,主要有兩個原因,if語句有一個特點,程式碼在哪裡停止(上面wait)就在哪裡起來。第二個特點就是notify()是隨機喚醒等待執行緒。這個隨機特點就造成了列印結果順序不可控。
2.if語句換成while語句,notify()方法換成notifyAll()方法
if沒有每次都對flag進行判斷,但是while語句會每次都進行條件判斷。notifyAll()就是把全部的執行緒都喚醒,然後這些執行緒去判斷,再執行相關列印。
package thread; public class NotifyAll_Demo { public static void main(String[] args) { Printer2 p = new Printer2(); new Thread() { public void run() { while(true) { try { p.print1(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); new Thread() { public void run() { while(true) { try { p.print2(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); new Thread() { public void run() { while(true) { try { p.print3(); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } } class Printer2 { private int flag = 1; public void print1() throws InterruptedException { synchronized (this) { while(flag != 1) { this.wait(); // 設定執行緒等待,如果flag 不等於1 } System.out.print("跟"); System.out.print("我"); System.out.print("一"); System.out.print("起"); System.out.println("念"); flag = 2; this.notifyAll(); } } public void print2() throws InterruptedException { synchronized (this) { while(flag != 2) { this.wait(); // 設定執行緒等待,如果flag 不等於2 } System.out.print("做"); System.out.print("測"); System.out.print("試"); System.out.print("死"); System.out.print("路"); System.out.print("一"); System.out.println("條"); flag = 3; this.notifyAll(); } } public void print3() throws InterruptedException { synchronized (this) { while(flag != 3) { this.wait(); // 設定執行緒等待,如果flag 不等於2 } System.out.print("信"); System.out.print("才"); System.out.print("怪"); System.out.println("呢"); flag = 1; this.notifyAll(); } } }
執行效果:
跟我一起念
做測試死路一條
信才怪呢
跟我一起念
做測試死路一條
信才怪呢
跟我一起念
做測試死路一條
信才怪呢
跟我一起念
做測試死路一條
信才怪呢
跟我一起念
做測試死路一條
信才怪呢
...
多執行緒之間需要注意的問題
1.在同步程式碼塊中,用哪個物件鎖,就用哪個物件呼叫wait方法(例如上面的this)
2.為什麼wait方法和notify方法定義在Object這個類中
因為鎖物件可以是任意物件,Object是所有類的基類,所以wait方法和notify方法需要定義在Object類中
3.sleep方法和wait方法的區別
第一個區別:sleep方法必須傳入引數,引數就是時間,時間到了自動醒來。wait方法可以傳入引數也可以不傳入引數,傳入引數就是在引數時間結束後等待,不傳入引數就是直接等待。
第二個區別:sleep方法在同步函式或者同步程式碼塊中,不釋放鎖。wait方法在同步函式或者同步程式碼塊中,釋放鎖。