天天看點

JDK1.8源碼解析之SynchronousQueue

SynchronousQueue源碼解析

  • 前言

    這是一個阻塞隊列,每一個線程的插入操作都要等待另一個對應的删除操作。這個同步隊列沒有任何的容量,甚至沒有一個元素的容量。你不能檢視同步隊列中的元素,因為想要檢視一個元素必須是在正在嘗試删除一個元素的時候。你不能向隊列中插入元素,除非有另外一個線程正在删除隊列中的元素。這個隊列不能使用疊代器疊代周遊元素,因為這個同步隊列中沒有元素可以周遊。它們非常适合于切換設計,在這種設計中,在一個線程中運作的對象必須與在另一個線程中運作的對象同步,以便傳遞一些資訊、事件或任務。這個隊列支援可選的公平政策對生産者線程池和消費者線程排序,預設情況下是非公平政策,設定了公平政策可以保證先進先出的順序。

    JDK1.8源碼解析之SynchronousQueue

    非公平政策和公平政策的執行形式基本上是一樣的,非公平政策隊列用的是stack(Lifo後進先出),公平政策隊列用的是queue(Fifo先進先出),先進先出隊列在競争下通常支援更高的吞吐量,但是Lifo在公共應用程式中維護更高的線程局部性。

    公平政策和非公平政策的隊列都繼承了一個靜态抽象内部類Transferer,兩種政策都要實作transfer方法。

    先來看看非公平政策模式

    非公平政策的stack中的節點使用是SNode,來看一下這個靜态内部類?

SNode的變量

volatile SNode next;        // next node in stack
volatile SNode match;       // the node matched to this
volatile Thread waiter;     // to control park/unpark
Object item;                // data; or null for REQUESTs
int mode;
           

FIFO類型的SynchronousQueue的執行流程

JDK1.8源碼解析之SynchronousQueue

FIFO源碼比較簡單,這裡就不在介紹,LIFO類型的就是比較饒了,我是琢磨了一段時間才弄明白,不同的任務之間是如何進行比對的,并且在比對過程中是如何完成資料傳遞的。

這裡先直接上代碼,講解完代碼之後,在總結一個流程圖,加油,相信自己隻有熬過最難熬的過程,說明你才有機會進步。

LIFO的意思使用的是棧的資料結構,所有具有後進先出的效果。

LIFO的主類是TransferStack,

有4個重要的變量:

JDK1.8源碼解析之SynchronousQueue

這三個變量用來表示棧中節點SNODE的模式,REQUEST 表示請求擷取資料的操作,DATA 表示想隊列中添加資料的操作,FULFILLING 表示目前SNODE正在比對的過程中,希望其他線程不要幹擾。

JDK1.8源碼解析之SynchronousQueue

這個變量是棧的頭結點,這裡使用的是volatile,在多線程并發的環境下實作同步,對多線程保持可見性。

在來看看棧中節點的資料結構是啥樣的?

JDK1.8源碼解析之SynchronousQueue

在來看看核心方法transfer(),由于代碼比較長,是以這裡分三種情況進行解析。

  1. 目前棧為空或者目前的節點的模式和head節點的模式相同,那麼目前節點入棧等待比對。
    JDK1.8源碼解析之SynchronousQueue
  2. 目前節點和head節點的模式是互補的,也就是說一個節點是到棧取元素的,一個節點是到棧添加元素的節點,判斷這兩個節點互補的方法是isFulfilling(int),先來看看這個是如何實作的?
    JDK1.8源碼解析之SynchronousQueue

    m是NODE的模式,隻有三種0 、1、2

    看一下計算結果:

    0 & 2 即 0000 & 0010 = 0000 = 0

    1 & 2 即 0001 & 0010 = 0000 = 0

    2 & 2 即 0010 & 0010 = 0010 = 2

    是以 isFulfilling 隻有m=2時,才會傳回true,這個标志位的作用是顯示的表明目前節點正在完成比對,其他線程請勿幹擾。

    需要注意的地方就是,當目前節點正處于比對的時候,這個時候目前節點的模式的計算公式如下:

    JDK1.8源碼解析之SynchronousQueue

    計算結果如下:

    0 | 2 即 0000 | 0010 = 0010 = 2

    1 | 2 即 0001 | 0010 = 0011 = 3

    2 | 2 即 0010 | 0010 = 0010 = 2

    接下來就可以解析源碼啦?有點困了,額,在加把勁!!!

    JDK1.8源碼解析之SynchronousQueue
    最後看一看,node進入阻塞是怎麼處理的?
    JDK1.8源碼解析之SynchronousQueue
    整個LIFO的transfer至此已經完成,JDK1.8源碼解析又完成了一個,開始進入下一個吧!!!

繼續閱讀