1. 程式人生 > >執行緒間通訊與協作方式之——wait-notify機制

執行緒間通訊與協作方式之——wait-notify機制

大家好,上篇文章為大家介紹了執行緒間通訊和協作的一些基本方式,那這篇文章就來介紹一下經典的wait-notify機制吧。

什麼是wait-notify機制?

想象一下有兩個執行緒A、B,如果業務場景中需要這兩個執行緒交替執行任務(比如A執行完一次任務後換B執行,B執行完後再換A執行這樣重複交替),之前的基本通訊方式只能讓執行緒暫停一段指定時間,Join方法也無法做到這種交替執行的要求,那怎麼辦呢?

別急,針對這種場景java同樣為我們提供了一種經典的執行緒通訊方式——wait-notify機制,這裡涉及到下面的三個方法(關於鎖的知識後文會詳細講):

wait方法:當前執行緒在呼叫wait方法會先讓出當前執行緒持有的物件鎖以便讓其他執行緒能夠獲取,然後當前執行緒會停止執行並進入WAITING狀態。直到接收到喚醒或中斷訊號後,當前執行緒才會繼續嘗試獲取物件鎖。如果此時獲取物件鎖成功,就能繼續執行任務。

notify方法:當前執行緒的任務即將執行完畢併發出喚醒訊號,此時只有接收到喚醒訊號的執行緒才會嘗試獲取物件鎖。當然此時可能獲取物件鎖會失敗,因為notify方法不會即時釋放鎖,而是需要等到執行緒執行完畢後才會真正釋放鎖。

notifyAll方法:和notify方法作用相似,唯一不同的就是該方法會對當前所有在等待這個物件鎖的執行緒發出喚醒訊號。至於最終是哪個執行緒搶到了物件鎖,就要看哪個執行緒比較“幸運”啦。

關於這幾個方法,還有以下兩點需要關注:

1.wait、notify、notifyAll這三個方法都是Object類中定義的,而Object類是所有類的父類,所以在java中的所有物件都會繼承這三個方法。

2.這三個方法必須在同步塊中被呼叫(之後會介紹同步塊),如果在同步塊之外呼叫這三個方法,java會丟擲java.lang.IllegalMonitorStateException這個異常。

基於wait-notify機制的單生產者-單消費者模型

上面已經介紹了wait-notify機制用到的方法以及需要注意的點,實際上針對這個機制,有一個非常著名、非常經典的模型——生產者消費者模型。

什麼是生產者-消費者模型呢?簡單來說就是這麼個場景:有兩種執行緒分別是生產者執行緒和消費者執行緒,還有一個固定大小的資源佇列。

生產者的任務是根據原料生產出產品,並將生產好的產品往佇列裡扔;消費者的任務呢就是從佇列裡面拿已經生產好的產品去進行包裝。

我們可以看到在這個場景中,因為佇列可容納的資源是有限的,所以當佇列滿時,生產者就沒辦法繼續往佇列裡放產品,此時生產者就需要等待消費者從佇列裡拿走產品後,才能繼續往佇列裡放產品;

而消費者也是一樣,當佇列為空時,消費者就無法從佇列裡拿到產品,此時就需要等待生產者成功生產出產品並往佇列裡扔,才能繼續從佇列裡拿產品。

這個場景是wait-notify機制最適合發揮作用的場景,下面是一個單生產者-單消費者的模擬程式碼:

 View Code

童鞋們可以執行程式碼試試,這裡資源池最大允許放10個產品。

這裡留一個問題給大家思考,如果我這裡的add和remove方法不加synchronized修飾,就會丟擲java.lang.IllegalMonitorStateException異常,那麼是什麼原因導致java必須要這麼做呢?我會在介紹synchronized關鍵字的時候公佈答案。

好了,wait-notify機制到這裡就介紹完畢,希望大家能夠理解。下篇文章會為大家講解一下volatile這個關鍵字的用法。