線程之間須要一些協調通訊,來共同完成一件任務。 java
多個線程在處理同一資源,可是任務卻不一樣。 編程
其中涉及的三個方法 : 多線程
wait():讓線程處於阻塞狀態,被wait的線程會被存儲到線程池中。notifyAll():喚醒線程池中的全部線程。 併發
這些方法都是final的,即它們都是不能被重寫的,不能經過子類覆寫去改變它們的行爲。
編程語言
關於等待/喚醒機制,要記住的關鍵點是:
必須從同步環境內調用wait()、notify()、notifyAll()方法。線程不能調用對象上等待或通知的方法,除非它擁有那個對象的鎖。
wait()、notify()、notifyAll()都是Object的實例方法。與每一個對象具備鎖同樣,每一個對象能夠有一個線程列表,他們等待來自該信號(通知)。線程經過執行對象上的wait()方法得到這個等待列表。從那時候起,它再也不執行任何其餘指令,直到調用對象的notify()方法爲止。若是多個線程在同一個對象上等待,則將只選擇一個線程(不保證以何種順序)繼續執行。若是沒有線程等待,則不採起任何特殊操做。 測試
當在對象上調用wait()方法時,執行該代碼的線程當即放棄它在對象上的鎖。然而調用notify()時,並不意味着這時線程會放棄其鎖。若是線程仍然在完成同步代碼,則線程在移出以前不會放棄鎖。所以,只要調用notify()並不意味着這時該鎖變得可用。 優化
無論任何編程語言,對於多線程程序來講,生產者和消費者模型都是最經典的。 this
實際上,準確說應該是「生產者-消費者-倉儲」模型,離開了倉儲,生產者消費者模型就顯得沒有說服力了。
對於此模型,應該明確一下幾點:
一、生產者僅僅在倉儲未滿時候生產,倉滿則中止生產。
二、消費者僅僅在倉儲有產品時候才能消費,倉空則等待。
三、當消費者發現倉儲沒產品可消費時候會通知生產者生產。
四、生產者在生產出可消費產品時候,應該通知等待的消費者去消費。 spa
/** * Java線程:併發協做-生產者消費者模型 * * @author leizhimin 2009-11-4 14:54:36 */ public class Test { public static void main(String[] args) { Godown godown = new Godown(30); Consumer c1 = new Consumer(50, godown); Consumer c2 = new Consumer(20, godown); Consumer c3 = new Consumer(30, godown); Producer p1 = new Producer(10, godown); Producer p2 = new Producer(10, godown); Producer p3 = new Producer(10, godown); Producer p4 = new Producer(10, godown); Producer p5 = new Producer(10, godown); c1.start(); c2.start(); c3.start(); p1.start(); p2.start(); p3.start(); p4.start(); p5.start(); } } /** * 倉庫 */ class Godown { public static final int max_size = 100; //最大庫存量 public int curnum; //當前庫存量 Godown() { } Godown(int curnum) { this.curnum = curnum; } /** * 生產指定數量的產品 * * @param neednum */ public synchronized void produce(int neednum) { //測試是否須要生產 while (neednum + curnum > max_size) { System.out.println("要生產的產品數量" + neednum + "超過剩餘庫存量" + (max_size - curnum) + ",暫時不能執行生產任務!"); try { //當前的生產線程等待 wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //知足生產條件,則進行生產,這裏簡單的更改當前庫存量 curnum += neednum; System.out.println("已經生產了" + neednum + "個產品,現倉儲量爲" + curnum); //喚醒在此對象監視器上等待的全部線程 notifyAll(); } /** * 消費指定數量的產品 * * @param neednum */ public synchronized void consume(int neednum) { //測試是否可消費 while (curnum < neednum) { try { //當前的生產線程等待 wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //知足消費條件,則進行消費,這裏簡單的更改當前庫存量 curnum -= neednum; System.out.println("已經消費了" + neednum + "個產品,現倉儲量爲" + curnum); //喚醒在此對象監視器上等待的全部線程 notifyAll(); } } /** * 生產者 */ class Producer extends Thread { private int neednum; //生產產品的數量 private Godown godown; //倉庫 Producer(int neednum, Godown godown) { this.neednum = neednum; this.godown = godown; } public void run() { //生產指定數量的產品 godown.produce(neednum); } } /** * 消費者 */ class Consumer extends Thread { private int neednum; //生產產品的數量 private Godown godown; //倉庫 Consumer(int neednum, Godown godown) { this.neednum = neednum; this.godown = godown; } public void run() { //消費指定數量的產品 godown.consume(neednum); } }
該例中使用了notifyAll()方法喚醒了全部方法,其實最好只喚醒對方便可。 咱們將在後期章節中對其優化。 線程