生產者消費者問題描述是有一塊緩衝區做爲倉庫,生產者能夠將產品放入倉庫,消費者則能夠從其取走產品。java
1. 同步:效率高易實現,代碼控制性較好,經常使用;安全
2. 管道:不易控制,被傳輸數據對象不易於封裝,實用性不強。(PipedInputStream/PipedOutputStream)併發
(1)核心:保證同一資源被多個線程併發訪問時的完整性。函數
(2)方法:測試
1. wait()/notify()this
2. await()/signal()spa
3. BlockingQueue阻塞隊列方法.net
是基類Object的兩個方法,意味着全部Java類均可擁有,故可爲任何對象實現同步機制。線程
wait():當緩衝區已滿/空時,對應線程中止本身的執行,放棄鎖,等待;設計
notify():線程執行後,向其餘等待的線程發出可執行的通知,同時放棄鎖,等待。
【代碼】倉庫使用時生產者與消費者同步synchronized(倉庫),同步代碼內根據條件(同步對象.wait¬ify),不直接在生產者類Producer和消費者類Consumer中實現這兩個方法,用Storage類中的實現:
可徹底取代wait() / nofity(),但同時鎖定機制Lock直接
掛鉤,具備更大的靈活性。在Lock對象調用newCondition()方法,將條件變量和一個鎖對象進行綁定,進而控制併發程序訪問競爭資源的安全。
只須要更新倉庫類Storage的代碼便可,生產者Producer、消費者Consumer、測試類Test的代碼均不須要進行任何更改。這樣咱們就知道爲神馬我要在Storage類中定義public void produce(int num);和public void consume(int num);方法,並在生產者類Producer和消費者類Consumer中調用Storage類中的實現了吧。將可能發生的變化集中到一個類中,不影響原有的構架設計,同時無需修改其餘業務層代碼。
BlockingQueue是JDK5.0的新增內容,它是一個已經在內部實現了同步的隊列,實現方式採用的是咱們第2種await() / signal()方法。它能夠在生成對象時指定容量大小。它用於阻塞操做的是put()和take()方法。
put()方法:相似於咱們上面的生產者線程,容量達到最大時,自動阻塞。
take()方法:相似於咱們上面的消費者線程,容量爲0時,自動阻塞。
固然,你會發現這時對於public void produce(int num);和public void consume(int num);方法業務邏輯上的實現跟前面兩個例子不太同樣,不要緊,這個例子只是爲了說明BlockingQueue阻塞隊列的使用。
有時使用BlockingQueue可能會出現put()和System.out.println()輸出不匹配的狀況,這是因爲它們之間沒有同步形成的。當緩衝區已滿,生產者在put()操做時,put()內部調用了await()方法,放棄了線程的執行,而後消費者線程執行,調用take()方法,take()內部調用了signal()方法,通知生產者線程能夠執行,導致在消費者的println()還沒運行的狀況下生產者的println()先被執行,因此有了輸出不匹配的狀況。
對於BlockingQueue你們能夠放心使用,這可不是它的問題,只是在它和別的對象之間的同步有問題。