天天看點

java 生産者消費者問題 并發問題的解決(轉)

引言

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

java 生産者消費者問題 并發問題的解決(轉)

生産者消費者圖

  存儲空間已滿,而生産者占用着它,消費者等着生産者讓出空間進而去除産品,生産者等着消費者消費産品,進而向空間中添加産品。互相等待,進而發生死鎖。

  生産者消費者問題是研究多線程程式時繞不開的經典問題之一,它描述是有一塊緩沖區作為倉庫,生産者可以将産品放入倉庫,消費者則可以從倉庫中取走産品。解決生産者/消費者問題的方法可分為兩類:

  (1)采用某種機制保護生産者和消費者之間的同步;

  (2)在生産者和消費者之間建立一個管道。

  第一種方式有較高的效率,并且易于實作,代碼的可控制性較好,屬于常用的模式。第二種管道緩沖區不易控制,被傳輸資料對象不易于封裝等,實用性不強。是以本文隻介紹同步機制實作的生産者/消費者問題。

  同步問題核心在于:如何保證同一資源被多個線程并發通路時的完整性。常用的同步方法是采用信号或加鎖機制,保證資源在任意時刻至多被一個線程通路。Java語言在多線程程式設計上實作了完全對象化,提供了對同步機制的良好支援。在Java中一共有四種方法支援同步,其中前三個是同步方法,一個是管道方法。

(1)wait() / notify()方法

(2)await() / signal()方法

(3)BlockingQueue阻塞隊列方法

(4)PipedInputStream / PipedOutputStream

本文隻介紹最常用的前兩種種,第三、四種暫不做讨論,有興趣的讀者可以自己去網上找答案。

一、wait() / notify()方法

  wait() / nofity()方法是基類Object的兩個方法,也就意味着所有Java類都會擁有這兩個方法,這樣,我們就可以為任何對象實作同步機制。

  wait()方法:當緩沖區已滿/空時,生産者/消費者線程停止自己的執行,放棄鎖,使自己處于等等狀态,讓其他線程執行。

  notify()方法:當生産者/消費者向緩沖區放入/取出一個産品時,向其他等待的線程發出可執行的通知,同時放棄鎖,使自己處于等待狀态。

代碼實作:

1、倉庫類

java 生産者消費者問題 并發問題的解決(轉)
java 生産者消費者問題 并發問題的解決(轉)

2、生産者

java 生産者消費者問題 并發問題的解決(轉)
java 生産者消費者問題 并發問題的解決(轉)

3、消費者

java 生産者消費者問題 并發問題的解決(轉)
java 生産者消費者問題 并發問題的解決(轉)

4、測試類

java 生産者消費者問題 并發問題的解決(轉)
java 生産者消費者問題 并發問題的解決(轉)

5、結果:

java 生産者消費者問題 并發問題的解決(轉)
java 生産者消費者問題 并發問題的解決(轉)

  看完上述代碼,對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的代碼均不需要進行任何更改。

倉庫類

java 生産者消費者問題 并發問題的解決(轉)
java 生産者消費者問題 并發問題的解決(轉)

結果:

java 生産者消費者問題 并發問題的解決(轉)
java 生産者消費者問題 并發問題的解決(轉)

  

  這樣我們就知道為神馬我要在Storage類中定義public void produce(int num);和public void consume(int num);方法,并在生産者類Producer和消費者類Consumer中調用Storage類中的實作了吧。将可能發生的變化集中到一個類中,不影響原有的構架設計,同時無需修改其他業務層代碼。

總結

  兩種方式原理一緻,都是對獨占空間加鎖,阻塞和喚醒線程,第一種方式比較傳統,第二種方式速度比較快。

http://www.cnblogs.com/0201zcr/p/4758533.html