1、wait()、notify/notifyAll() 方法是Object的本地final方法,無法被重寫。
2、wait()使當前線程阻塞,前提是 必須先獲得鎖,一般配合synchronized 關鍵字使用,即,一般在synchronized 同步代碼塊里使用 wait()、notify/notifyAll() 方法。
3、 由於 wait()、notify/notifyAll() 在synchronized 代碼塊執行,說明當前線程一定是獲取了鎖的。
當線程執行wait()方法時候,會釋放當前的鎖,然后讓出CPU,進入等待狀態。
只有當 notify/notifyAll() 被執行時候,才會喚醒一個或多個正處於等待狀態的線程,然后繼續往下執行,直到執行完synchronized 代碼塊的代碼或是中途遇到wait() ,再次釋放鎖。
也就是說,notify/notifyAll() 的執行只是喚醒沉睡的線程,而不會立即釋放鎖,鎖的釋放要看代碼塊的具體執行情況。所以在編程中,盡量在使用了notify/notifyAll() 后立即退出臨界區,以喚醒其他線程讓其獲得鎖
4、wait() 需要被try catch包圍,以便發生異常中斷也可以使wait等待的線程喚醒。
5、notify 和wait 的順序不能錯,如果A線程先執行notify方法,B線程在執行wait方法,那么B線程是無法被喚醒的。
6、notify 和 notifyAll的區別
notify方法只喚醒一個等待(對象的)線程並使該線程開始執行。所以如果有多個線程等待一個對象,這個方法只會喚醒其中一個線程,選擇哪個線程取決於操作系統對多線程管理的實現。notifyAll 會喚醒所有等待(對象的)線程,盡管哪一個線程將會第一個處理取決於操作系統的實現。如果當前情況下有多個線程需要被喚醒,推薦使用notifyAll 方法。比如在生產者-消費者里面的使用,每次都需要喚醒所有的消費者或是生產者,以判斷程序是否可以繼續往下執行。
7、在多線程中要測試某個條件的變化,使用if 還是while?
要注意,notify喚醒沉睡的線程后,線程會接着上次的執行繼續往下執行。所以在進行條件判斷時候,可以先把 wait 語句忽略不計來進行考慮;顯然,要確保程序一定要執行,並且要保證程序直到滿足一定的條件再執行,要使用while進行等待,直到滿足條件才繼續往下執行。如下代碼:
1 public class K { 2 //狀態鎖
3 private Object lock; 4 //條件變量
5 private int now,need; 6 public void produce(int num){ 7 //同步
8 synchronized (lock){ 9 //當前有的不滿足需要,進行等待,直到滿足條件
10 while(now < need){ 11 try { 12 //等待阻塞
13 lock.wait(); 14 } catch (InterruptedException e) { 15 e.printStackTrace(); 16 } 17 System.out.println("我被喚醒了!"); 18 } 19 // 做其他的事情
20 } 21 } 22 } 23
顯然,只有當前值滿足需要值的時候,線程才可以往下執行,所以,必須使用while 循環阻塞。注意,wait() 當被喚醒時候,只是讓while循環繼續往下走.如果此處用if的話,意味着if繼續往下走,會跳出if語句塊。
8、實現生產者和消費者問題
什么是生產者-消費者問題呢?

如上圖,假設有一個公共的容量有限的池子,有兩種人,一種是生產者,另一種是消費者。需要滿足如下條件:
1、生產者產生資源往池子里添加,前提是池子沒有滿,如果池子滿了,則生產者暫停生產,直到自己的生成能放下池子。
2、消費者消耗池子里的資源,前提是池子的資源不為空,否則消費者暫停消耗,進入等待直到池子里有資源數滿足自己的需求。
- 倉庫類
1 import java.util.LinkedList; 2
3 /**
4 * 生產者和消費者的問題 5 * wait、notify/notifyAll() 實現 6 */
7 public class Storage1 implements AbstractStorage { 8 //倉庫最大容量
9 private final int MAX_SIZE = 100; 10 //倉庫存儲的載體
11 private LinkedList list = new LinkedList(); 12
13 //生產產品
14 public void produce(int num){ 15 //同步
16 synchronized (list){ 17 //倉庫剩余的容量不足以存放即將要生產的數量,暫停生產
18 while(list.size()+num > MAX_SIZE){ 19 System.out.println("【要生產的產品數量】:" + num + "\t【庫存量】:"
20 + list.size() + "\t暫時不能執行生產任務!"); 21
22 try { 23 //條件不滿足,生產阻塞
24 list.wait(); 25 } catch (InterruptedException e) { 26 e.printStackTrace(); 27 } 28 } 29
30 for(int i=0;i<num;i++){ 31 list.add(new Object()); 32 } 33
34 System.out.println("【已經生產產品數】:" + num + "\t【現倉儲量為】:" + list.size()); 35
36 list.notifyAll(); 37 } 38 } 39
40 //消費產品
41 public void consume(int num){ 42 synchronized (list){ 43
44 //不滿足消費條件
45 while(num > list.size()){ 46 System.out.println("【要消費的產品數量】:" + num + "\t【庫存量】:"
47 + list.size() + "\t暫時不能執行生產任務!"); 48
49 try { 50 list.wait(); 51 } catch (InterruptedException e) { 52 e.printStackTrace(); 53 } 54 } 55
56 //消費條件滿足,開始消費
57 for(int i=0;i<num;i++){ 58 list.remove(); 59 } 60
61 System.out.println("【已經消費產品數】:" + num + "\t【現倉儲量為】:" + list.size()); 62
63 list.notifyAll(); 64 } 65 } 66 }
- 抽象倉庫類
1 public interface AbstractStorage { 2 void consume(int num); 3 void produce(int num); 4 }
- 生產者
1 public class Producer extends Thread{ 2 //每次生產的數量
3 private int num ; 4
5 //所屬的倉庫
6 public AbstractStorage abstractStorage; 7
8 public Producer(AbstractStorage abstractStorage){ 9 this.abstractStorage = abstractStorage; 10 } 11
12 public void setNum(int num){ 13 this.num = num; 14 } 15
16 // 線程run函數
17 @Override 18 public void run() 19 { 20 produce(num); 21 } 22
23 // 調用倉庫Storage的生產函數
24 public void produce(int num) 25 { 26 abstractStorage.produce(num); 27 } 28 }
- 消費者
1 public class Consumer extends Thread{ 2 // 每次消費的產品數量
3 private int num; 4
5 // 所在放置的倉庫
6 private AbstractStorage abstractStorage1; 7
8 // 構造函數,設置倉庫
9 public Consumer(AbstractStorage abstractStorage1) 10 { 11 this.abstractStorage1 = abstractStorage1; 12 } 13
14 // 線程run函數
15 public void run() 16 { 17 consume(num); 18 } 19
20 // 調用倉庫Storage的生產函數
21 public void consume(int num) 22 { 23 abstractStorage1.consume(num); 24 } 25
26 public void setNum(int num){ 27 this.num = num; 28 } 29 }
- 測試
1 public class Test{ 2 public static void main(String[] args) { 3 // 倉庫對象
4 AbstractStorage abstractStorage = new Storage1(); 5
6 // 生產者對象
7 Producer p1 = new Producer(abstractStorage); 8 Producer p2 = new Producer(abstractStorage); 9 Producer p3 = new Producer(abstractStorage); 10 Producer p4 = new Producer(abstractStorage); 11 Producer p5 = new Producer(abstractStorage); 12 Producer p6 = new Producer(abstractStorage); 13 Producer p7 = new Producer(abstractStorage); 14
15 // 消費者對象
16 Consumer c1 = new Consumer(abstractStorage); 17 Consumer c2 = new Consumer(abstractStorage); 18 Consumer c3 = new Consumer(abstractStorage); 19
20 // 設置生產者產品生產數量
21 p1.setNum(10); 22 p2.setNum(10); 23 p3.setNum(10); 24 p4.setNum(10); 25 p5.setNum(10); 26 p6.setNum(10); 27 p7.setNum(80); 28
29 // 設置消費者產品消費數量
30 c1.setNum(50); 31 c2.setNum(20); 32 c3.setNum(30); 33
34 // 線程開始執行
35 c1.start(); 36 c2.start(); 37 c3.start(); 38
39 p1.start(); 40 p2.start(); 41 p3.start(); 42 p4.start(); 43 p5.start(); 44 p6.start(); 45 p7.start(); 46 } 47 }
- 輸出
【要消費的產品數量】:50 【庫存量】:0 暫時不能執行生產任務! 【要消費的產品數量】:20 【庫存量】:0 暫時不能執行生產任務! 【要消費的產品數量】:30 【庫存量】:0 暫時不能執行生產任務! 【已經生產產品數】:10 【現倉儲量為】:10 【要消費的產品數量】:30 【庫存量】:10 暫時不能執行生產任務! 【要消費的產品數量】:20 【庫存量】:10 暫時不能執行生產任務! 【要消費的產品數量】:50 【庫存量】:10 暫時不能執行生產任務! 【已經生產產品數】:10 【現倉儲量為】:20 【已經生產產品數】:10 【現倉儲量為】:30 【要消費的產品數量】:50 【庫存量】:30 暫時不能執行生產任務! 【已經消費產品數】:20 【現倉儲量為】:10 【要消費的產品數量】:30 【庫存量】:10 暫時不能執行生產任務! 【已經生產產品數】:10 【現倉儲量為】:20 【要消費的產品數量】:50 【庫存量】:20 暫時不能執行生產任務! 【要消費的產品數量】:30 【庫存量】:20 暫時不能執行生產任務! 【已經生產產品數】:10 【現倉儲量為】:30 【已經消費產品數】:30 【現倉儲量為】:0 【要消費的產品數量】:50 【庫存量】:0 暫時不能執行生產任務! 【已經生產產品數】:10 【現倉儲量為】:10 【要消費的產品數量】:50 【庫存量】:10 暫時不能執行生產任務! 【已經生產產品數】:80 【現倉儲量為】:90 【已經消費產品數】:50 【現倉儲量為】:40