1. 程式人生 > >深入理解wait/notify/notifyAll的作用

深入理解wait/notify/notifyAll的作用

notify: 喚醒在監視器物件上等待的單個執行緒,此時呼叫該方法的程式碼繼續執行。 notifyAll: 喚醒在監視器物件上等待的所有執行緒,此時呼叫該方法的程式碼繼續執行。 第一、為什麼會有wait/notify/notifyAll這幾個方法? (1) wait/notify/notifyAll是為了避免輪詢(嘗試執行)帶來的效能損失,這句話是什麼意思?看下面的講解: 為了說清道理,我們用“圖書館借書”這個經典例子來作解釋。 在簡單的synchrnozed 同步機制下,李四如果想借,先要去圖書館檢視書有沒有還回來。 李四是個心急的人,他每天都去圖書館查;而張三看書看得慢,過了半個月才把書還回來, 結果李四在這半個月裡全都白跑了,浪費了不少交通車費。 而如果使用wait/notify機制,李四就不用白忙了。 他第一次去圖書館時發現書已借走,就回家靜靜等待(wait); 張三把書還掉後,通知(notify)李四,李四去圖書館拿書即可。整個過程中,李四沒有白跑,沒浪費錢。 書 ---- 臨界資源,需互斥地訪問 張三,李四 ---- 兩個競爭的執行緒 坐車去圖書館查書 ---- 輪詢 車費 ---- CPU空間 等待 ---- wait 通知下一個借書者 ---- notify 也就是說,若使用簡單的synchonized機制實現互斥,會導致執行緒主動發起輪詢,若N次輪詢沒有成功,就產生了N次的CPU空間浪費; 如果加上了 wait/notify機制,就可以避免這些無謂的輪詢,節省CPU的消耗。 (2) wait/notify/notifyAll可以控制執行緒執行與不執行。 第二、為什麼wait/notify/notifyAll方法一定要寫在synchronized裡面呢? 因為第一點已經說了wait/notify/notifyAll的作用是為了避免輪詢帶來的效能損失, 而產生輪詢的條件是多個執行緒對同一個資源進行操作。 第三、為什麼wait/notify/notifyAll方法定義在Object類裡面呢? 因為wait/notify/notifyAll必須寫在synchronized裡面,而synchronized的物件鎖可以是任意物件, 所以wait/notify/notifyAll方法定義在Object類裡面呢。 呼叫wait/notify/notifyAll方法的物件,必須和synchronized()的物件鎖一致。 第四、 看一個wait和notify示例: // 這個示例是不需要flag標記的: public class Resource { public String name; public String sex; public boolean flag = true; public static void main(String[] args) { Resource r = new Resource(); Input in = new Input(r); Thread t1 = new Thread(in); Output out = new Output(r); Thread t2 = new Thread(out); t1.start(); t2.start(); } } class Input implements Runnable { Resource r; public Input(Resource r) { this.r = r; } public void run() { int x = 0; while (true) { synchronized (r) { try { Thread.sleep(1000); } catch (InterruptedException e1) { e1.printStackTrace(); } if (x == 0) { r.name = "謝霆鋒"; r.sex = "男人"; } else { r.name = "張柏芝"; r.sex = "女人"; } // 注意:呼叫notify方法後,喚醒了等待的執行緒。 // 但這裡程式碼繼續執行,並不是馬上交給其它執行緒執行,除非cpu時間片結束。 r.notify(); try { r.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } x = (x + 1) % 2; } } } class Output implements Runnable { Resource r; public Output(Resource r) { this.r = r; } public void run() { while (true) { synchronized (r) { System.out.println("--------------" + r.name + "是" + r.sex); r.notify(); try { r.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } -------------------------------------------------------------- 這裡要注意,再次強調: wait/notify/notifyAll方法要放在synchronized裡面,除此之外,還要非常非常注意一個重點: 呼叫wait/notify/notifyAll方法的物件,必須要和synchronized的物件鎖是一致的。 如,以下程式碼是錯誤的: class MyThread extends Thread { public static Object obj = new Object(); public void run() { synchronized(obj) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } this.notify(); } } }