JUC學習之 生產者消費者問題

2022-05-05 03:48:11 字數 3757 閱讀 3874

前面寫過一篇關於生產者消費者問題的部落格,但是通過對juc的學習,發現前面寫的存在不少問題,比如使用synchronized鎖,以及沒有做好防止執行緒虛假喚醒的措施,故在此重新完善。

前面對reentranlock的加鎖解鎖原理的源**進行了一些分析,synchronized和reentranlock的比較分析也大致說了一下。

大概的對比如下:

資源類:store類,用於生產和消費,是生產者和消費者的共享資源。對這個類的操作需要呼叫lock()/unlock()方法進行加鎖和解鎖。

假設有生產者a和消費者d分別進行生產和消費,當商品數量<10時,生產者a可以生產,每當生產一件商品時,嘗試喚醒消費者d進行消費,當商品數量達到10時,生產者停止生產;當商品數量》0時,消費者d可以消費,每當消費一件商品時,嘗試喚醒生產者執行緒進行生產,當商品數量為0時,消費者停止消費。

我們假設上述過程進行20輪,即每個生產者生產20次商品,每個消費者消費20次商品。

上述過程用**描述如下:

class

store

//每生產1件商品,就可以通知消費者來消費,即喚醒所有執行緒

system.out.

println

(thread.

currentthread()

.getname()

+"\t生產了第"+(

++count)

+"件商品");

condition.

signalall()

;}catch

(exception e)

finally

}//消費商品的方法

public

void

decrement()

//每消費一件商品,就可以通知生產者來生產,即喚醒所有執行緒

system.out.

println

(thread.

currentthread()

.getname()

+"\t消費了第"

+(count--)+

"件商品");

condition.

signalall()

;}catch

(exception e)

finally}}

public

class

produceconsumerdemo1

//建立生產者並建立和啟動執行緒

private

static

void

createproducer

(store store, string producer)

}, producer)

.start()

;}//建立消費者並建立和啟動執行緒

private

static

void

createconsumer

(store store, string consumer)

}, consumer)

.start();}}

上述程式執行結果如下圖:

可以發現執行正常,生產者執行緒a和消費者執行緒d先後訪問資源。按照程式設計的一樣,初始商品數量為0,則消費者執行緒d被阻塞,生產者執行緒a進行生產,當商品數量到達10個時,生產者執行緒a自行阻塞,釋放鎖。此時被喚醒的消費者執行緒d獲得鎖進行消費。

這樣看來似乎已經解決了生產者消費者問題,但是這段程式中存在著乙個漏洞

//當商品數量為0時,阻塞當前消費者執行緒

if(count ==0)

//商品數量到達10件時,阻塞當前生產者執行緒

if(count ==10)

這兩段**存在著很嚴重的問題,也許當前乙個生產者乙個消費者,我們看不出來有什麼問題,但是如果存在兩個生產者執行緒a、b以及兩個消費者執行緒c、d,我們就能發現有以下錯誤:

發現出現了負數,出現這樣的原因是某個生產者執行緒生產完一件商品後,執行了signalall()方法,這樣就將其他所有阻塞狀態的執行緒喚醒,包括兩個消費者執行緒,在其中乙個消費者執行緒執行正常消費過程後,此時的商品餘量為0,然而另乙個消費者執行緒搶占鎖,繼續進行消費,這樣便使得出現了負數,導致這樣的結果的原因是

if

(count ==0)

最初,初始狀態下(即生產者尚未生產商品,商品數量為0)兩個消費者執行緒經過if()的判斷,都被阻塞,當生產者生產完畢時,喚醒兩個消費者執行緒,這兩個消費者執行緒便不需要再次判斷當前是否還有商品,當乙個消費者執行緒進行了正常消費,另乙個消費者執行緒直接跳出if分支,搶占鎖進行消費,便出現了這種錯誤。反過來,兩個生產者執行緒也會出現上述類似的錯誤:

商品數量到達10後,其中乙個生產者執行緒釋放鎖,另乙個生產者執行緒直接跳出if語句,搶占鎖進行繼續生產。

上述程式出現的這些錯誤我們將其統稱為**執行緒的虛假喚醒**。

面對這個執行緒虛假喚醒,解決措施是,將if改成while,在第二個消費者進行消費前,或者第二個生產者進行生產前,再次判斷當前的商品數量是否達到閾值,若達到了則繼續阻塞該執行緒。這樣便可以有效解決因虛假喚醒造成的資料異常。

修改後的**如下:

class

store

system.out.

println

(thread.

currentthread()

.getname()

+"\t生產了第"+(

++count)

+"件商品");

condition.

signalall()

;}catch

(exception e)

finally

}public

void

decrement()

system.out.

println

(thread.

currentthread()

.getname()

+"\t消費了第"

+(count--)+

"件商品");

condition.

signalall()

;}catch

(exception e)

finally}}

public

class

produceconsumerdemo1

private

static

void

createconsumer

(store store, string consumer)

}, consumer)

.start()

;}private

static

void

createproducer

(store store, string producer)

}, producer)

.start();}}

執行結果如下:

可以發現程式執行的資料正常,至此,對於生產者消費者的進一步分析暫告一段落。

生產者消費者問題

public class producer consumer class godown public godown int num public synchronized void produce int n catch interruptedexception e curr num n syste...

生產者 消費者問題

在學習程序互斥中,有個著名的問題 生產者 消費者問題。這個問題是乙個標準的 著名的同時性程式設計問題的集合 乙個有限緩衝區和兩類執行緒,它們是生產者和消費者,生產者把產品放入緩衝區,相反消費者便是從緩衝區中拿走產品。生產者在緩衝區滿時必須等待,直到緩衝區有空間才繼續生產。消費者在緩衝區空時必 須等待...

生產者 消費者問題

1 程序互斥問題 緩衝區b是臨界資源,程序p和c不能同時對b進行操作,即只能互斥的操作 2 程序同步問題 p不能往 滿 的的緩衝區b放產品,c不能從空的緩衝區獲得產品。當緩衝區滿時,c必須先於p執行,當緩衝區空時,p必須先於c執行 我們給出如下基於記錄型 二元 訊號量機制的解法 10 9 2013 ...