在CLH鎖核心思想的影響下,JDK併發包以CLH鎖做爲基礎而設計,其中主要是考慮到CLH鎖更容易實現取消與超時功能。比起原來的CLH鎖已經作了很大的改造,主要從兩方面進行了改造:節點的結構與節點等待機制。javascript
在結構上引入了頭結點和尾節點,他們分別指向隊列的頭和尾,嘗試獲取鎖、入隊列、釋放鎖等實現都與頭尾節點相關,而且每一個節點都引入前驅節點和後後續節點的引用;在等待機制上由原來的自旋改爲阻塞喚醒。如圖,經過前驅後續節點的引用一節節鏈接起來造成一個鏈表隊列,對於頭尾節點的更新必須是原子的。下面詳細看看入隊、檢測掛起、釋放出隊、超時、取消等操做。html
整塊邏輯實際上是用一個無限循環進行CAS操做,即用自旋方式競爭直到成功。將尾節點tail的舊值賦予新節點node的前驅節點,並嘗試CAS操做將新節點node賦予尾節點tail,原先的尾節點的後續節點指向新建節點node。完成上面步驟就創建起一條如圖所示的鏈表隊列。代碼簡化以下:java
for (;;) {
Node t = tail;
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return node;
}
}複製代碼
上面咱們說到節點等待機制已經被JDK併發做者由自旋機制改形成阻塞機制,一個新建的節點完成入隊操做後,若是是自旋則直接進入循環檢測前驅節點是否爲頭結點便可,但如今被改成阻塞機制,當前線程將首先檢測是否爲頭結點且嘗試獲取鎖,若是當前節點爲頭結點併成功獲取鎖則直接返回,當前線程不進入阻塞,不然將當前線程阻塞。代碼簡化以下:node
for (;;) {
if (node.prev == head)
if(嘗試獲取鎖成功){
head=node;
node.next=null;
return;
}
阻塞線程
}複製代碼
出隊的主要工做是負責喚醒等待隊列中後續節點,讓全部等待節點環環相接,每條線程有序地往下執行。代碼簡化以下:併發
Node s = node.next;
喚醒節點s包含的線程複製代碼
在支持超時的模式下須要LockSupport類的parkNanos方法支持,線程在阻塞一段時間後會自動喚醒,每次循環將累加消耗時間,當總消耗時間大於等於自定義的超時時間時就直接分返。代碼簡化以下:this
for (;;) {
嘗試獲取鎖
if (nanosTimeout <= 總消耗時間)
return;
LockSupport.parkNanos(this, nanosTimeout);
}複製代碼
隊列中等待鎖的隊列可能由於中斷或超時而涉及到取消操做,這種狀況下被取消的節點再也不進行鎖競爭。此過程主要完成的工做是將取消的節點移除,先將節點的。先將節點node狀態設置成取消,再將前驅節點pred的後續節點指向node的後續節點,這裏因爲涉及到競爭,必須經過CAS進行操做,CAS操做就算失敗也沒必要理會,由於已經改了節點的狀態,在嘗試獲取鎖操做中會循環對節點的狀態判斷。spa
node.waitStatus = Node.CANCELLED;
Node pred = node.prev;
Node predNext = pred.next;
Node next = node.next;
compareAndSetNext(pred, predNext, next);複製代碼
====廣告時間,可直接跳過====線程
鄙人的新書《Tomcat內核設計剖析》已經在京東預售了,有須要的朋友能夠到 item.jd.com/12185360.ht… 進行預約。感謝各位朋友。設計
=========================code
歡迎關注: