當前位置:
首頁 > 知識 > Java多線程詳解——生產者和消費者

Java多線程詳解——生產者和消費者

這篇文章根據生產者和消費者的模型來介紹一下Java多線程的一些知識。

我們這裡的生產者和消費者模型為:

生產者Producer 生產某個對象(共享資源),放在緩衝池中,然後消費者從緩衝池中取出這個對象。也就是生產者生產一個,消費者取出一個。這樣進行循環。

第一步:我們先創建共享資源的類 Person,它有兩個方法,一個生產對象,一個消費對象

Java多線程詳解——生產者和消費者

第二步:創建生產者線程,並在 run() 方法中生產50個對象

Java多線程詳解——生產者和消費者

第三步:創建消費者線程,並在 run() 方法中消費50個對象

Java多線程詳解——生產者和消費者

由於我們的模型是生產一個,馬上消費一個,那期望的結果便是 Tom---11,Marry--21,Tom---11,Mary---21...... 連續這樣交替出現50次

但是結果卻是:

Java多線程詳解——生產者和消費者

為了讓結果產生的更加明顯,我們在共享資源的 pop() 和 push() 方法中添加一段延時代碼

Java多線程詳解——生產者和消費者

這個時候,結果如下:

Java多線程詳解——生產者和消費者

結果分析:這時候我們發現結果全亂套了,Marry--21是固定的,Tom--11是固定的,但是上面的結果全部亂了,那這又是為什麼呢?而且有很多重複的數據連續出現,那這又是為什麼呢?

原因1:出現錯亂數據,是因為先生產出Tom--21,但是消費者沒有消費,然後生產者繼續生產出name為Marry,而消費者這個時候拿去消費了,那麼便出現 Marry--11。同理也會出現 Tom--21

原因2:出現重複數據,是因為生產者生產一份數據了,消費者拿去消費了,但是第二次生產者生產數據了,但是消費者沒有去消費;而第三次生產者繼續生產數據,消費者才開始消費,這便會產生重複

解決辦法1:生產者生產name和age必須要是一個整體一起完成,即同步。生產的中間不能讓消費者來消費即可。便不會產生錯亂的數據。

這裡我們選擇同步方法(在方法前面加上 synchronized)

Java多線程詳解——生產者和消費者

Java多線程詳解——生產者和消費者

結果如下:

Java多線程詳解——生產者和消費者

問題:還是沒有解決上面的問題2,出現重複的問題。期望的結果是 Tom---11,Marry--21,Tom---11,Mary---21...... 連續這樣交替出現50次。那如何解決呢?

解決辦法:生產者生產一次數據了,就暫停生產者線程,等待消費者消費;消費者消費完了,消費者線程暫停,等待生產者生產數據,這樣來進行。

這裡我們介紹一個同步鎖池的概念:

同步鎖池:同步鎖必須選擇多個線程共同的資源對象,而一個線程獲得鎖的時候,別的線程都在同步鎖池等待獲取鎖;當那個線程釋放同步鎖了,其他線程便開始由CPU調度分配鎖

關於讓線程等待和喚醒線程的方法,如下:(這是 Object 類中的方法)

Java多線程詳解——生產者和消費者

Java多線程詳解——生產者和消費者

wait():執行該方法的線程對象,釋放同步鎖,JVM會把該線程放到等待池中,等待其他線程喚醒該線程

notify():執行該方法的線程喚醒在等待池中等待的任意一個線程,把線程轉到鎖池中等待(注意鎖池和等待池的區別)

notifyAll():執行該方法的線程喚醒在等待池中等待的所有線程,把線程轉到鎖池中等待。

注意:上述方法只能被同步監聽鎖對象來調用,這也是為啥wait() 和 notify()方法都在 Object 對象中,因為同步監聽鎖可以是任意對象,只不過必須是需要同步線程的共同對象即可,否則別的對象調用會報錯:        java.lang.IllegalMonitorStateException

假設 A 線程和 B 線程同時操作一個 X 對象,A,B 線程可以通過 X 對象的 wait() 和 notify() 方法來進行通信,流程如下:

①、當線程 A 執行 X 對象的同步方法時,A 線程持有 X 對象的 鎖,B線程在 X 對象的鎖池中等待

②、A線程在同步方法中執行 X.wait() 方法時,A線程釋放 X 對象的鎖,進入 X 對象的等待池中

③、在 X 對象的鎖池中等待鎖的 B 線程獲得 X 對象的鎖,執行 X 的另一個同步方法

④、B 線程在同步方法中執行 X.notify() 方法,JVM 把 A 線程從等待池中移動到 X 對象的鎖池中,等待獲取鎖

⑤、B 線程執行完同步方法,釋放鎖,等待獲取鎖的 A 線程獲得鎖,繼續執行同步方法

那麼為了解決上面重複的問題,修改代碼如下:

Java多線程詳解——生產者和消費者

Java多線程詳解——生產者和消費者

Java多線程詳解——生產者和消費者

結果:

Java多線程詳解——生產者和消費者

那麼這便是我們期待的結果,交替出現。

死鎖:

①、多線程通信的時候,很容易造成死鎖,死鎖無法解決,只能避免

②、當 A 線程等待由 B 線程持有的鎖,而 B 線程正在等待由 A 線程持有的鎖時發生死鎖現象(比如A拿著鉛筆,B拿著圓珠筆,A說你先給我圓珠筆,我就把鉛筆給你,而B說你先給我鉛筆,我就把圓珠筆給你,這就造成了死鎖,A和B永遠不能進行交換)

③、JVM 既不檢測也不避免這種現象,所以程序員必須保證不能出現這樣的情況

Thread 類中容易造成死鎖的方法(這兩個方法都已經過時了,不建議使用):

suspend():使正在運行的線程放棄 CPU,暫停運行(不釋放鎖)

resume():使暫停的線程恢復運行

情景:A 線程獲得對象鎖,正在執行一個同步方法,如果 B線程調用 A 線程的 suspend() 方法,此時A 暫停運行,放棄 CPU 資源,但是不放棄同步鎖,那麼B也不能獲得鎖,A又暫停,那麼便造成死鎖。

解決死鎖法則:當多個線程需要訪問 共同的資源A,B,C時,必須保證每一個線程按照一定的順序去訪問,比如都先訪問A,然後B,最後C。就像我們這裡的生產者---消費者模型,制定了必須生產者先生產一個對象,然後消費者去消費,消費完畢,生產者才能在開始生產,然後消費者在消費。這樣的順序便不會造成死鎖。


學習Java的同學注意了!!!

學習過程中遇到什麼問題或者想獲取學習資源的話,歡迎加入Java學習交流群495273252,我們一起學Java!

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Java團長 的精彩文章:

SSH框架的整合詳解
提高逼格,給自己的網站加入智能聊天功能
黑客入門之單機遊戲外掛
最全DOS的CMD命令,程序員必會
深入理解Java註解類型

TAG:Java團長 |

您可能感興趣

從FaceBook數據泄密到作為數據生產者的個人
EOS區塊生產者Everipedia通過交易RAM獲利60萬美元
4K視頻的智能化生產者-帝視科技IV4KMaker
阻塞隊列實現生產者消費者模式
無印良品生開生鮮賣場,只為讓消費者體驗生產者的工作?
誰是地球上最大的生產者?
EOS的吸引力已經逐漸流失?EOS區塊生產者達成一致,降低賬戶創建費用以吸引新用戶
觀念攝影,不再是「圖像生產者」?
德國生產者物價指數年率;希臘經常賬
量數:用區塊鏈技術,讓價值回歸數據生產者
量數:用區塊鏈技術,讓價值回歸數據生產者|創業
微博宣布推出「潮汐計劃」,2年提供20億賦能內容生產者、助商業變現
它是黃體酮的「生產者」,女人常吃,乳腺無結節,增生「逃」走了
2019年會取消玉米生產者補貼嗎?
它是葉酸「生產者」,女人經常吃一點,補充雌激素預防疾病
從這些訂閱號生產者的焦慮中 我們還看到了什麼
我是個邊緣的圖像生產者
上古時期物質財富直接生產者所受的剝削到底屬於什麼性質?
人人都愛「吸貓」,而他們吸成了貓片生產者!
元氣騎士:3根瓦爾基里之羽 她不是羽毛的搬運工而是生產者!