要理解SynchronousQueue首先就是要知道它的作用,只有知道這個類別存在的目的和提供的功能,再能再讀原始碼的時候不迷路。
我們在之前的學習中,如果線程間要交換資料一般都是用一個透過公共變數或一個同步阻塞佇列,生產者執行緒設定變數或是往佇列中put值,消費者線程則讀取變數或從佇列take。
而SynchronousQueue則不需要儲存線程間交換的數據,它的作用更像是一個匹配器,使生產者和消費者一一匹配。
例如當一個執行緒呼叫了put方法時,發現佇列中沒有take執行緒,那麼put執行緒就會阻塞,當take執行緒進來時發現有阻塞的put線程,那麼他們兩個就會匹配上,然後take線程取得到put線程的數據,兩個線程都不阻塞。
反之一個執行緒呼叫take方法也會阻塞線程,當一個呼叫put方法的執行緒進來後也會與之匹配。
如果一個take或put執行緒進來發現有同類的take或put執行緒在阻塞中,那麼執行緒會排到後面,直到有不同類別的執行緒進來然後符合其中一個執行緒 。
透過流程描述相信對SynchronousQueue有了一定的了解,也知道SynchronousQueue為什麼不用儲存元素。
透過檢視SynchronousQueue的take與put方法發現都是呼叫的一個屬性transferer的transfer方法,而transferer屬性是SynchronousQueue的抽象靜態內部類別Transferer。 Transferer有兩個子類別TransferQueue和TransferStack;
在SynchronousQueue建構方法中透過傳遞的參數fair來判斷是建立TransferQueue還是TransferStack,透過參數fair來看TransferQueue應該是公平模式,那麼TransferStack就是非功能模式。
首先它有一個內部類別QNode,透過上面分析發現可能會出現多個消費者或多個生產者,他們就會形成一個隊列,而QNode就是用來組成一個隊列的鍊錶。
QNode主要有四個屬性:
QNode next:表示下一個節點;
Object item;這裡其實是put出去的數據,take方法產生的節點這裡為null;
Thread waiter;阻塞的線程,通常是生成這個節點的線程阻塞,其他線程進來獲取到了數據後會喚醒;
boolean isData:true則是put生成的,false表示是take生成的;
既然take與put都依賴transfer方法,那麼我們就來看transfer的實現,源碼太長就不貼出來了,直接看總結的流程圖:
因為這個方法並沒有採用鎖定來控制,所以在整個流程中還有很多判斷,這些都是次要的,這裡整理的是主要關鍵的流程。
簡單說明一下,整個流程就是依賴QNode鍊錶,QNode的isData來區分是take還是put方法,鍊錶中的節點的isData一定是相同的,QNode的item是take和put線程交換的數據,只不過take方法交換的數據是null。
可以看出來每次可以配對的時候都是拿的最前面的節點進來回傳資料。
同樣TransferStack也有一個鍊錶結構叫做SNode ,SNode 的主要屬性如下:
SNode next: 下一個節點;
SNode match:與之符合成功的節點;
Thread waiter:阻塞的執行緒;
Object item:要給出去的值;
int mode:節點分類,用來區分put還是take;
同樣整理了transfer的原始碼流程圖,具體如下圖:
同樣都是用鍊錶實現,但是這個首先結構不一樣,多一個match表示與之匹配的節點。
透過流程可以看到mode一個有三個值,除了0,1外還有一個2用來表示正在匹配中的節點。主要就是在上圖中紅框部分,在發現頭部節點不是進行中時,當前線程就會創建一個匹配中的節點,然後加到頭部,最後去和後面的節點匹配 。
如果發現正在符合中則會把符合的節點從鍊錶中移除。
透過流程分析可以看出TransferStack是後來進來的執行緒放到了頭部,會先進行配對。
以上是java的SynchronousQueue是什麼的詳細內容。更多資訊請關注PHP中文網其他相關文章!