引言
生産者和消費者問題是線程模型中的經典問題:生産者和消費者在同一時間段内共用同一個存儲空間,如下圖所示,生産者向空間裡存放資料,而消費者取用資料,如果不加以協調可能會出現以下情況:

生産者消費者圖
存儲空間已滿,而生産者占用着它,消費者等着生産者讓出空間進而去除産品,生産者等着消費者消費産品,進而向空間中添加産品。互相等待,進而發生死鎖。
生産者消費者問題是研究多線程程式時繞不開的經典問題之一,它描述是有一塊緩沖區作為倉庫,生産者可以将産品放入倉庫,消費者則可以從倉庫中取走産品。解決生産者/消費者問題的方法可分為兩類:
(1)采用某種機制保護生産者和消費者之間的同步;
(2)在生産者和消費者之間建立一個管道。
第一種方式有較高的效率,并且易于實作,代碼的可控制性較好,屬于常用的模式。第二種管道緩沖區不易控制,被傳輸資料對象不易于封裝等,實用性不強。是以本文隻介紹同步機制實作的生産者/消費者問題。
同步問題核心在于:如何保證同一資源被多個線程并發通路時的完整性。常用的同步方法是采用信号或加鎖機制,保證資源在任意時刻至多被一個線程通路。Java語言在多線程程式設計上實作了完全對象化,提供了對同步機制的良好支援。在Java中一共有四種方法支援同步,其中前三個是同步方法,一個是管道方法。
(1)wait() / notify()方法
(2)await() / signal()方法
(3)BlockingQueue阻塞隊列方法
(4)PipedInputStream / PipedOutputStream
本文隻介紹最常用的前兩種種,第三、四種暫不做讨論,有興趣的讀者可以自己去網上找答案。
一、wait() / notify()方法
wait() / nofity()方法是基類Object的兩個方法,也就意味着所有Java類都會擁有這兩個方法,這樣,我們就可以為任何對象實作同步機制。
wait()方法:當緩沖區已滿/空時,生産者/消費者線程停止自己的執行,放棄鎖,使自己處于等等狀态,讓其他線程執行。
notify()方法:當生産者/消費者向緩沖區放入/取出一個産品時,向其他等待的線程發出可執行的通知,同時放棄鎖,使自己處于等待狀态。
代碼實作:
1、倉庫類
2、生産者
3、消費者
4、測試類
5、結果:
看完上述代碼,對wait() / notify()方法實作的同步有了了解。你可能會對Storage類中為什麼要定義public void produce(int num);和public void consume(int num);方法感到不解,為什麼不直接在生産者類Producer和消費者類Consumer中實作這兩個方法,卻要調用Storage類中的實作呢?淡定,後文會有解釋。我們先往下走。
二、await() / signal()方法
在JDK5.0之後,Java提供了更加健壯的線程處理機制,包括同步、鎖定、線程池等,它們可以實作更細粒度的線程控制。await()和signal()就是其中用來做同步的兩種方法,它們的功能基本上和wait() / nofity()相同,完全可以取代它們,但是它們和新引入的鎖定機制Lock直接挂鈎,具有更大的靈活性。通過在Lock對象上調用newCondition()方法,将條件變量和一個鎖對象進行綁定,進而控制并發程式通路競争資源的安全。
下面來看代碼:
隻需要更新倉庫類Storage的代碼即可,生産者Producer、消費者Consumer、測試類Test的代碼均不需要進行任何更改。
倉庫類
結果:
這樣我們就知道為神馬我要在Storage類中定義public void produce(int num);和public void consume(int num);方法,并在生産者類Producer和消費者類Consumer中調用Storage類中的實作了吧。将可能發生的變化集中到一個類中,不影響原有的構架設計,同時無需修改其他業務層代碼。
總結
兩種方式原理一緻,都是對獨占空間加鎖,阻塞和喚醒線程,第一種方式比較傳統,第二種方式速度比較快。
http://www.cnblogs.com/0201zcr/p/4758533.html