本博客系列是學習併發編程過程當中的記錄總結。因爲文章比較多,寫的時間也比較散,因此我整理了個目錄貼(傳送門),方便查閱。html
併發編程系列博客傳送門java
wait方法是Object類中的一個方法,調用這個方法會讓調用線程進入waiting狀態,直到另外一個線程調用了當前對象上的notify()或者notifyAll()方法。(若是其餘線程調用了該線程的interrupt()方法,會中斷這種waiting狀態,同時該線程拋出InterruptedException異常返回)。若是當前線程已經獲取了鎖資源,調用wait方法以後會釋放這個鎖資源。編程
wait方法還有一個重載方法wait(long time),這個方法會等待time時間,若是在這個時間內沒有其餘線程來喚醒它的話,這個線程會本身喚醒繼續得到執行機會。併發
另外須要注意的是,若是調用wait()方法的線程沒有事先獲取該對象的監視器鎖,則調用wait()方法時調用線程會拋出IllegalMonitorStateException異常。app
notify方法會喚醒等待對象監視器的單個線程,若是等待對象監視器的有多個線程,則選取其中一個線程進行喚醒到底選擇喚醒哪一個線程是任意的,由CPU本身決定。框架
notify方法還有個兄弟方法notifyAll,這個方法會喚醒全部等待監視器對象的線程。ide
wait-notify模式的一個典型應用就是能夠實現生產者-消費者模式。讓我印象很深是我畢業那年阿里巴巴年校園招聘的一個筆試題:學習
有一個蘋果箱,有10我的向這個箱子中每次隨機放入一個蘋果,有10我的每次隨機從這個箱子中隨機拿走一個蘋果,同時須要知足箱子中的蘋果總數不能超過50個。請用代碼實現上面的場景(不能使用併發集合框架)this
如今看來,這道題不就是爲wait-notify模式量身打造的一道題目麼。當時水平有限,又急急忙忙的,因此記得當時寫的不太好。這邊從新整理下這個代碼線程
public class AppleBox { private int appleCount; public synchronized void putApple() { while (appleCount >= 50) { try { //會釋放鎖 this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } appleCount++; String name = Thread.currentThread().getName(); System.out.println("[" + name + "]放入一個,當前盒子中蘋果數:" + appleCount); this.notifyAll(); } public synchronized void takeApple() { while (appleCount <= 0) { try { //會釋放鎖 this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } appleCount--; String name = Thread.currentThread().getName(); System.out.println("[" + name + "]拿走一個,當前盒子中蘋果數:" + appleCount); this.notifyAll(); } private static class AppleTaker implements Runnable { private AppleBox appleBox; public AppleTaker(AppleBox appleBox) { this.appleBox = appleBox; } @Override public void run() { while (true) { appleBox.takeApple(); try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } } private static class ApplePutter implements Runnable { private AppleBox appleBox; public ApplePutter(AppleBox appleBox) { this.appleBox = appleBox; } @Override public void run() { while (true) { appleBox.putApple(); try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) { AppleBox appleBox = new AppleBox(); for (int i = 0; i < 20; i++) { Thread t = new Thread(new ApplePutter(appleBox)); t.setName("ApplePutter:" + i); t.start(); } for (int i = 0; i < 20; i++) { Thread t = new Thread(new AppleTaker(appleBox)); t.setName("AppleTaker:" + i); t.start(); } } }
執行結果以下:
[ApplePutter:0]放入一個,當前盒子中蘋果數:1 [ApplePutter:1]放入一個,當前盒子中蘋果數:2 [ApplePutter:5]放入一個,當前盒子中蘋果數:3 [ApplePutter:9]放入一個,當前盒子中蘋果數:4 [ApplePutter:13]放入一個,當前盒子中蘋果數:5 [ApplePutter:2]放入一個,當前盒子中蘋果數:6 [ApplePutter:6]放入一個,當前盒子中蘋果數:7 [ApplePutter:10]放入一個,當前盒子中蘋果數:8 [ApplePutter:17]放入一個,當前盒子中蘋果數:9 [ApplePutter:14]放入一個,當前盒子中蘋果數:10 [ApplePutter:18]放入一個,當前盒子中蘋果數:11 [ApplePutter:3]放入一個,當前盒子中蘋果數:12 [ApplePutter:7]放入一個,當前盒子中蘋果數:13 [ApplePutter:11]放入一個,當前盒子中蘋果數:14 [ApplePutter:8]放入一個,當前盒子中蘋果數:15 [ApplePutter:15]放入一個,當前盒子中蘋果數:16 [ApplePutter:19]放入一個,當前盒子中蘋果數:17 [ApplePutter:4]放入一個,當前盒子中蘋果數:18 [AppleTaker:3]拿走一個,當前盒子中蘋果數:17 [ApplePutter:12]放入一個,當前盒子中蘋果數:18 [AppleTaker:1]拿走一個,當前盒子中蘋果數:17 [AppleTaker:5]拿走一個,當前盒子中蘋果數:16 [ApplePutter:16]放入一個,當前盒子中蘋果數:17 [AppleTaker:0]拿走一個,當前盒子中蘋果數:16 [AppleTaker:12]拿走一個,當前盒子中蘋果數:15 [AppleTaker:8]拿走一個,當前盒子中蘋果數:14 [AppleTaker:16]拿走一個,當前盒子中蘋果數:13 [AppleTaker:7]拿走一個,當前盒子中蘋果數:12 [AppleTaker:11]拿走一個,當前盒子中蘋果數:11 [AppleTaker:19]拿走一個,當前盒子中蘋果數:10 [AppleTaker:9]拿走一個,當前盒子中蘋果數:9 [AppleTaker:13]拿走一個,當前盒子中蘋果數:8 [AppleTaker:2]拿走一個,當前盒子中蘋果數:7 [AppleTaker:6]拿走一個,當前盒子中蘋果數:6 [AppleTaker:10]拿走一個,當前盒子中蘋果數:5 [AppleTaker:14]拿走一個,當前盒子中蘋果數:4 [AppleTaker:4]拿走一個,當前盒子中蘋果數:3 [AppleTaker:15]拿走一個,當前盒子中蘋果數:2 [AppleTaker:18]拿走一個,當前盒子中蘋果數:1 [AppleTaker:17]拿走一個,當前盒子中蘋果數:0 [ApplePutter:0]放入一個,當前盒子中蘋果數:1 [ApplePutter:1]放入一個,當前盒子中蘋果數:2 [ApplePutter:5]放入一個,當前盒子中蘋果數:3 [ApplePutter:9]放入一個,當前盒子中蘋果數:4 [ApplePutter:13]放入一個,當前盒子中蘋果數:5 [ApplePutter:17]放入一個,當前盒子中蘋果數:6 [ApplePutter:2]放入一個,當前盒子中蘋果數:7 [ApplePutter:6]放入一個,當前盒子中蘋果數:8 [ApplePutter:10]放入一個,當前盒子中蘋果數:9 [ApplePutter:14]放入一個,當前盒子中蘋果數:10 [ApplePutter:18]放入一個,當前盒子中蘋果數:11 [ApplePutter:3]放入一個,當前盒子中蘋果數:12 [ApplePutter:7]放入一個,當前盒子中蘋果數:13 [ApplePutter:11]放入一個,當前盒子中蘋果數:14 [ApplePutter:15]放入一個,當前盒子中蘋果數:15 [ApplePutter:19]放入一個,當前盒子中蘋果數:16 [AppleTaker:3]拿走一個,當前盒子中蘋果數:15 [ApplePutter:4]放入一個,當前盒子中蘋果數:16 [ApplePutter:8]放入一個,當前盒子中蘋果數:17 [ApplePutter:12]放入一個,當前盒子中蘋果數:18
生產者和消費者的邏輯均可以統一抽象成如下幾個步驟:
僞代碼以下:
synchronized(對象) { while(條件){ 對象.wait(); } //進行生產或者消費活動 doSomething(); 對象.notifyAll(); }