一,AQS
1,AQS是JUC的核心功能組件,主要通過FIFO雙向鏈表特性對進行park()和unpark()操作來實現線程阻塞和線程喚醒。AQS主要提供了兩種功能,獨佔和共享。
* 獨佔鎖:獨佔鎖,每次只能有一個線程持有鎖,比如ReentrantLock 就是以獨佔方式實現的互斥鎖
* 共享鎖:允許多個線程同時持有鎖,併發訪問共享資源,比如ReentrantReadWriteLock.ReadLock,CountDownLatch
2,類圖
* 從主體結構看,AQS繼承自 AbstractOwnableSynchronizer(獲取鎖的當前線程獲取和設置),並實現了序列化接口。AQS內部定義了Node和ConditionObject兩個內部類。其中Node類是構建AQS的FIFO雙向鏈表的基礎數據結構;ConditionObject實現了Condition接口,是基於AQS進行線程通信的基本類。
* 其他所有JUC內涉及併發處理的類,基本的處理邏輯都是基於定義內部類Sync並繼承AQS類,對AQS內部部分接口進行重寫,變形爲各自的業務需求;如:ReentrantLock,ReentrantReadWriteLock,CountDownLatch,Semaphore等等...
二,AQS的內部實現
1,,AQS是一個FIFO(First Inner First Outer)的雙向鏈表,這種結構的特點是每個節點都有兩個指針,分別指向當前節點的前置節點和後置節點。所以可以從任何一個節點很方便的訪問前後節點,每一個節點由AQS內部類Node表示。在存在線程競爭時,每一個未競爭到鎖的線程都會被封裝成Node節點存儲到AQS同步隊列中。噹噹前獲取到鎖的線程釋放鎖後,會從隊列中喚醒下一個阻塞的節點,以此類推。
2,Node結構如下:
* Node節點的幾種狀態
// 表示共享鎖
static final Node SHARED = new Node();
// 表示獨佔鎖
static final Node EXCLUSIVE = null;
// 表示當前節點失效
static final int CANCELLED = 1;
// 節點初始狀態,表示在FIFO雙向鏈表構成的同步隊列中等待
static final int SIGNAL = -1;
// Condition單向鏈表隊列狀態
static final int CONDITION = -2;
//
static final int PROPAGATE = -3;
3,AQS阻塞隊列添加節點
* 新的線程封裝成Node節點添加到AQS同步隊列中,設置前置節點(prev)爲之前的tail節點,並將tail節點的next節點指向當前節點
* 通過CAS將tail節點指向當前節點
* 此處注意是先設置的prev節點,所以在後續遍歷處理的時候,是從後往前遍歷,就是因爲這個原因。因爲從前往後,可能存在next指向命令並沒有執行
4,AQS阻塞隊列釋放節點
* 當存在線程釋放鎖後,AQS的頭結點(head)會獲取鎖,並被AQS阻塞隊列釋放。
* 修改head節點爲head指向的next節點,即下一個獲得鎖的節點,此處CAS不需要加鎖,因爲是加鎖的線程完成的
* 修改新head節點的prev節點,將prev的指針指向null
三,AQS獨佔鎖源碼分析
* AQS內部沒有公平鎖和非公平鎖的劃分,所謂公平鎖和非公平鎖是部分業務代碼內部實現(如ReentrantLock)
1,從一段獨佔鎖代碼開始
package com.gupao.concurrent;
import java.util.concurrent.locks.ReentrantLock;
/**
* 獨佔鎖演示
* @author pj_zhang
* @create 2019-10-01 14:28
**/
public class ExclusiveTest {
// 定義全局重入鎖對象
private static final ReentrantLock reentrantLock = new ReentrantLock();
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
// 遍歷並啓動100道線程
Thread thread = new Thread(() -> {
try {
// 線程內部加鎖並沉睡業務秒數,模擬獲取到鎖後不立即釋放
reentrantLock.lock();
Thread.sleep(3000);
System.out.println(
Thread.currentThread().getName() + "獲取鎖,執行時間: "
+ System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// finally塊中釋放鎖
reentrantLock.unlock();
}
}, "THREAD_" + i);
thread.start();
System.out.println(thread.getName() + "啓動成功...");
}
}
}
* 從代碼中我們可以看到,100道線程已經全部啓動,並以三秒的頻率依次執行,那這裏面就會有兩個疑問
* 已經啓動但是被加鎖沒有立即執行的線程是以什麼樣的方式存在?
* 獲得鎖的線程執行完成後,如何通知未獲得鎖的線程進行線程爭搶並執行?
2,嘗試獲取鎖
2.1,不帶時間獲取鎖
* tryAcquire(int arg)
// 代碼內部直接拋異常,說明該部分代碼依託子類實現
// AQS的子類實現在各個鎖業務代碼中處理,後續會逐一分析
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
2.2,帶時間獲取鎖 -- 該部分屬於底層類,後續業務代碼調用不再分析
* tryAcquireNanos(int arg, long nanosTimeout)
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
// 判斷當前線程是否已經被中斷
// 線程中斷,直接拋出異常
if (Thread.interrupted())
throw new InterruptedException();
// 通過 tryAcquire(arg) 先直接嘗試獲取鎖,獲取到直接返回true
// tryAcquire(arg) 需要在子類中實現,此處已經不做具體分析
// 沒有獲取到後,通過 doAcquireNanos 進行限時時間內獲取
return tryAcquire(arg) ||
doAcquireNanos(arg, nanosTimeout);
}
* doAcquireNanos(int arg, long nanosTimeout)
private boolean doAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (nanosTimeout <= 0L)
return false;
// 獲取當前自旋結束時間,通過當前納秒數+超時納秒數
final long deadline = System.nanoTime() + nanosTimeout;
// 封裝當前線程爲Node節點,並添加的AQS同步隊列隊尾
// 此處注意Node節點的waitStatus狀態爲EXCLUSIVE,即表示獨佔
final Node node = addWaiter(Node.EXCLUSIVE);
// 是否獲取到鎖標誌位
boolean failed = true;
try {
for (;;) {
// 獲取當前節點的上一個節點
final Node p = node.predecessor();
// 如果上一個節點爲head節點,並且繼續嘗試獲取鎖成功,則當前線程獲取到鎖
// FIFO隊列默認存在一個head節點(不存在則第一次會創建),該頭結點爲空
if (p == head && tryAcquire(arg)) {
// 獲取到鎖後,將當前節點置爲頭結點,並清空頭節點內容
setHead(node);
// 將頭節點的next指向置爲空,處理後則p節點掛空,會被GC回收
p.next = null; // help GC
// 設置failed狀態爲false,說明競爭鎖成功
failed = false;
return true;
}
// 如果沒有獲取到鎖,則用過期時間減去當前時間,判斷剩餘時間是否合法
nanosTimeout = deadline - System.nanoTime();
// 如果剩餘時間小於0,時間不合法,說明線程未獲取到鎖
if (nanosTimeout <= 0L)
return false;
// 剩餘時間>0時處理
// shouldParkAfterFailedAcquire 判斷當前節點是否可以掛起,
// nanosTimeout > spinForTimeoutThreshold 表示如果線程剩餘時間大於1000納秒,則線程直接掛起nanosTimeout納秒
if (shouldParkAfterFailedAcquire(p, node) &&
nanosTimeout > spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanosTimeout);
// 如果線程已經中斷,則線程掛起
if (Thread.interrupted())
throw new InterruptedException();
}
} finally {
// 如果自旋完成後,已經沒有獲取到鎖,則將當前節點waitStatus置爲失效
if (failed)
cancelAcquire(node);
}
}
* addWaiter(Node mode):添加當前節點到AQS同步隊列,該方法是一個常用底層方法,後續不再分析!這部分也是線程在AQS同步隊列中的存在方式,mode會被封裝爲Node節點的nextWaiter屬性,標識當前屬性的共享還是獨佔
private Node addWaiter(Node mode) {
// 包裝當前線程爲Node對象,mode爲傳遞的Node.EXCLUSIVE,表示獨佔鎖
// Node.EXCLUSIVE節點被包裝爲Node節點的nextWaiter屬性,用於後續判斷
Node node = new Node(Thread.currentThread(), mode);
// 將tail節點標識爲前置節點
Node pred = tail;
// 判斷前置節點(即tail節點)是否爲空
// 不爲空表示同步隊列已經創建
if (pred != null) {
// 設置當前節點的前置節點爲剛纔賦值的尾結點
node.prev = pred;
// 通過CAS將tail節點替換爲node節點
if (compareAndSetTail(pred, node)) {
// 將前置節點(即原tail節點)的next節點設置爲當前節點
// 此處注意順序問題,即先向前指向,再向後指向
// 所以在遍歷時統一向上遍歷,防止向下遍歷時,存在併發導致只創建了前指,還沒有創建後指時遍歷不到
pred.next = node;
return node;
}
}
// 如果爲空,則表示同步隊列還沒有創建,則進行創建
enq(node);
return node;
}
* enq(final Node node):添加節點時,如果同步隊列爲空,則進行創建
private Node enq(final Node node) {
// 自旋進行創建
for (;;) {
// 繼續獲取尾結點
Node t = tail;
// 尾結點爲空時,
// 此處如果直接不爲空說明存在併發
if (t == null) {
// 創建一個空節點表示頭節點,並將頭節點指向尾結點
// 如果創建失敗,說明存在併發已經創建了頭節點,繼續自旋走非空處理
// 如果創建成功,此時同步隊列中只有初始化的這一個節點,此時頭節點和尾結點相同
if (compareAndSetHead(new Node()))
tail = head;
// 尾結點不爲空時
} else {
// 將尾結點設置爲當前節點的上一個節點
node.prev = t;
// CAS設置當前節點爲新的尾結點
if (compareAndSetTail(t, node)) {
// 設置原尾結點的下一個節點爲當前節點
// 此處繼續重視雙向設置的前後問題
t.next = node;
// 最終返回上一個節點,此處在部分業務類中有應用
return t;
}
}
}
}
* setHead(Node node):獲取鎖成功後,重新修改頭節點,並將頭節點置空
private void setHead(Node node) {
// 設置當前節點爲頭結點
head = node;
// 設置當前節點的thread爲空,頭結點只表示爲一個空節點
node.thread = null;
// 設置當前節點的前置節點爲空
node.prev = null;
}
* shouldParkAfterFailedAcquire(Node pred, Node node):判斷線程在競爭鎖失敗後是否可以掛起;線程掛起後,waitStatus爲-1
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
// 獲取前置節點 waitStatus
int ws = pred.waitStatus;
// 前置節點 waitStatus 表示還在等待中,則直接掛起當前線程
if (ws == Node.SIGNAL)
return true;
// 前置節點 waitStatus > 0,說明 waitStatus 的狀態爲失效狀態(CANCELLED = 1)
if (ws > 0) {
// 遍歷清除所有失效的前置狀態,直到找到上一個不失效的前置節點,標位當前節點前置
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
// 並把遍歷到的前置節點的next節點指向當前節點
pred.next = node;
} else {
// 前置節點未失效,設置前置節點狀態爲等待,
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
// 前置節點爲其他狀態,基於前置節點可以快速執行完,則繼續自旋等待
return false;
}
* cancelAcquire(Node node):自旋完成後已經沒有獲取到鎖,或線程自旋過程中被中斷
private void cancelAcquire(Node node) {
if (node == null)
return;
node.thread = null;
// 自Node節點向上循環,提出已經失效節點
// 循環到最近的一個非失效節點停止
Node pred = node.prev;
while (pred.waitStatus > 0)
node.prev = pred = pred.prev;
// 獲取重新賦值的前置節點的下一個節點
Node predNext = pred.next;
// 將當前節點的節點狀態 waitStatus 置爲失效
node.waitStatus = Node.CANCELLED;
// 如果當前節點爲尾結點,則設置尾結點爲剛纔遍歷到的前置節點
// 並將前置節點的next節點置爲null
if (node == tail && compareAndSetTail(node, pred)) {
compareAndSetNext(pred, predNext, null);
// 當前節點爲中間節點
} else {
int ws;
// 此處一串判斷,主要爲了判斷前置節點是否頭節點
// 如果前置節點不是頭節點,且在等待中,則將前置節點的 next 節點設置爲當前節點的 next 節點
// 當前節點被掛空移除
if (pred != head &&
((ws = pred.waitStatus) == Node.SIGNAL ||
(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
pred.thread != null) {
Node next = node.next;
if (next != null && next.waitStatus <= 0)
compareAndSetNext(pred, predNext, next);
} else {
// 前置節點爲頭結點,則可能已經執行完釋放鎖並喚醒下一個線程
// 但是由於當前操作已經被鏈表進行了重新構造,則直接喚醒一個線程執行
// 下一個線程被喚醒後,會再次嘗試獲取鎖,獲取到則執行,獲取不到則繼續掛起
unparkSuccessor(node);
}
node.next = node; // help GC
}
}
* unparkSuccessor(Node node):喚醒下一個有效線程
private void unparkSuccessor(Node node) {
// 獲取當前節點的 waitStatus
int ws = node.waitStatus;
// 如果當前節點狀態還在等待中,則修改爲0,表示線程執行中
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
// 獲取當前線程的下一個節點,表示將要喚醒的節點
Node s = node.next;
// 如果下一個節點爲空,或者節點失效
if (s == null || s.waitStatus > 0) {
s = null;
// 如果鏈表存在斷層,從尾結點開始向上找,找到有效節點賦值給需要喚醒的節點
// 向上一直找到當前節點,則表示鏈表遍歷了一遍,找到的有效節點即爲node的下一個有效節點
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
// 有效喚醒節點不爲空,則喚醒當前節點所在的線程
if (s != null)
LockSupport.unpark(s.thread);
}
3,加鎖及加鎖失敗後的線程park
* acquire(int arg)
public final void acquire(int arg) {
// tryAcquire(arg):首先嚐試獲取鎖
// addWaiter(Node.EXCLUSIVE):嘗試獲取鎖失敗,封裝當前線程爲Node對象,並添加到AQS同步隊列尾部,注意此處傳遞參數爲獨佔鎖,之前已經分析
// acquireQueued:線程掛起以及線程再次鎖競爭
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
* acquireQueued(final Node node, int arg):獨佔鎖被喚醒後,不會嘗試去喚醒下一個節點線程
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
// 此處自旋,線程掛起後會阻塞
// 等線程被喚醒,則繼續循環嘗試獲取鎖,獲取鎖失敗,繼續當前流程
// 獲取鎖成功,則繼續處理線程
for (;;) {
// 獲取當前節點的前置節點
final Node p = node.predecessor();
// 前置節點爲head節點時,再次嘗試獲取鎖
if (p == head && tryAcquire(arg)) {
// 獲取鎖成功後,將當前節點置爲頭節點
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
// shouldParkAfterFailedAcquire:獲取鎖失敗後,判斷線程是否可以掛起,之前已經分析
// parkAndCheckInterrupt:線程掛起,並判斷線程是否已經中斷,如果中斷,設置中斷標識爲true
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
// 發生異常後,設置當前節點爲失效,之前已經分析
if (failed)
cancelAcquire(node);
}
}
* parkAndCheckInterrupt()
private final boolean parkAndCheckInterrupt() {
// 當前線程掛起
LockSupport.park(this);
// 返回當前線程中斷狀態,注意:此處調用後,線程中斷狀態爲復位,即無論當前狀態是什麼,返回結果後,會自動改爲false
return Thread.interrupted();
}
* 線程掛起後,會等待頭節點線程釋放後,進行線程喚醒
4,嘗試釋放鎖
* tryRelease(int arg):該方法爲實現類方法, 在具體實現類中再具體分析
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
5,釋放鎖及釋放後下一個節點喚醒
* release(int arg):釋放當前線程鎖狀態
public final boolean release(int arg) {
// 首先嚐試釋放鎖,釋放鎖成功
if (tryRelease(arg)) {
// 獲取頭節點,判斷頭節點狀態
// 存在等待節點的waitStatus肯定<0
Node h = head;
if (h != null && h.waitStatus != 0)
// 喚醒下一個等待節點
unparkSuccessor(h);
return true;
}
return false;
}
6,喚醒後的線程繼續執行加鎖流程
* acquireQueued(final Node node, int arg):線程喚醒後,繼續自旋獲取鎖,獲取鎖成功後,隔離頭節點,並將當前節點設置爲頭節點!接第三步
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
// 再次自旋,嘗試獲取鎖成功
if (p == head && tryAcquire(arg)) {
// 當節點節點設置爲頭節點,並將頭節點掛空,等待GC回收
// 此處注意不會喚醒下一個節點線程
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
// 初次自旋,當前線程掛起
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
四,AQS共享鎖源碼分析
1,從一段共享鎖代碼開始
package com.gupao.concurrent;
import java.util.concurrent.CountDownLatch;
/**
* 共享鎖代碼演示
* @author pj_zhang
* @create 2019-10-01 14:36
**/
public class ShardTest {
// 構建countDownLatch,進行全局共享鎖處理
// 傳遞參數爲線程數量,當數量通過countDown減爲0時,自定喚醒所有共享鎖
private static final CountDownLatch countDownLatch = new CountDownLatch(3);
public static void main(String[] args) {
// 循環構建線程數量,與countDownLatch限定數量一致
for (int i = 0; i < 3; i++) {
Thread thread = new Thread(() -> {
try {
// 標識線程已經激動,並直接進行等待,等待countDownLatch喚醒
System.out.println(Thread.currentThread().getName()
+ "進入等待。。。");
// 通過countDownLatch等待
countDownLatch.await();
Thread.sleep(1000);
// 線程被喚醒後,打印線程執行時間,此時阻塞線程會基本同時執行
// 存在共享鎖線程喚醒及線程調度時間(基本可以忽略)
System.out.println(Thread.currentThread().getName()
+ "執行,時間:" + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "THREAD_" + i);
thread.start();
countDownLatch.countDown();
}
}
}
* 從共享鎖線程演示可以看出,無論是代碼還是線程執行方式明顯與獨佔鎖不一致,那這裏面又會有疑問
* 基於共享鎖的線程阻塞是如何實現的?
* 共享鎖的鎖釋放如何實現共享鎖下的所有線程幾乎同時執行(線程調度及線程池阻塞耗時不計算在內)?
2,嘗試獲取鎖
2.1,不帶時間獲取鎖
* tryAcquireShared(int arg):同樣,共享鎖嘗試獲取,基於實現類實現
protected int tryAcquireShared(int arg) {
throw new UnsupportedOperationException();
}
2.2,帶時間獲取鎖
* tryAcquireSharedNanos(int arg, long nanosTimeout)
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
throws InterruptedException {
// 判斷線程是否中斷,中斷直接拋異常
if (Thread.interrupted())
throw new InterruptedException();
// tryAcquireShared:嘗試獲取共享鎖,此處依舊通過實現類實現
// doAcquireSharedNanos:嘗試釋放共享鎖,在指定納秒內
return tryAcquireShared(arg) >= 0 ||
doAcquireSharedNanos(arg, nanosTimeout);
}
* doAcquireSharedNanos(int arg, long nanosTimeout)
private boolean doAcquireSharedNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (nanosTimeout <= 0L)
return false;
final long deadline = System.nanoTime() + nanosTimeout;
// 包裝當前線程爲Node節點,注意此處傳遞參數爲Node.SHARED,表示共享鎖
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
// 自旋獲取鎖
for (;;) {
// 獲取上一個節點
final Node p = node.predecessor();
// 前置節點爲頭節點
if (p == head) {
// 嘗試獲取共享鎖
int r = tryAcquireShared(arg);
// 共享鎖獲取成功
if (r >= 0) {
// 設置當前節點爲頭節點,並遞歸喚醒後續共享節點,此部分後續分析
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return true;
}
}
// 後續定時與獨佔鎖一致
nanosTimeout = deadline - System.nanoTime();
if (nanosTimeout <= 0L)
return false;
if (shouldParkAfterFailedAcquire(p, node) &&
nanosTimeout > spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanosTimeout);
if (Thread.interrupted())
throw new InterruptedException();
}
} finally {
if (failed)
// 異常後,節點失效
cancelAcquire(node);
}
}
3,加鎖及加鎖失敗後的線程park
* acquireShared(int arg):獲取共享鎖
public final void acquireShared(int arg) {
// 嘗試獲取共享鎖,<0說明獲取失敗,該部分在子類中實現
if (tryAcquireShared(arg) < 0)
// 進行鎖獲取處理
doAcquireShared(arg);
}
* doAcquireShared(int arg):包裝Node節點,並添加到AQS同步隊列後線程掛起,喚醒後繼續嘗試加鎖執行!加鎖成功後,會判斷下一個節點是否共享鎖節點,如果是,會繼續喚醒!
private void doAcquireShared(int arg) {
// 包裝Node對象,注意此處傳遞mode爲Node.SHARED
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
// 獲取當前節點的前置節點
final Node p = node.predecessor();
if (p == head) {
// 前置節點爲頭節點,繼續嘗試獲取共享鎖
int r = tryAcquireShared(arg);
if (r >= 0) {
// 鎖獲取成功,設置頭節點,並喚醒下一個爲共享鎖的節點
// 此處注意建立鏈式喚醒思想,每一個線程都在park部分被阻塞,每一個線程被喚醒後都會走這一步進行鎖處理,所以每一道都會進行下一個喚醒
// 對共享鎖來說,則存在第一個節點喚醒後,會一直鏈式喚醒下去,不會在存在阻塞
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
// shouldParkAfterFailedAcquire:獲取鎖失敗後,判斷當前線程是否可以掛起
// parkAndCheckInterrupt:線程掛起
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
* setHeadAndPropagate(Node node, int propagate):設置頭節點並喚醒下一個共享鎖節點
private void setHeadAndPropagate(Node node, int propagate) {
// 獲取頭節點,並將當前節點設置爲頭節點
Node h = head;
setHead(node);
// 條件判斷爲有效節點
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
// 判斷下一個節點是否爲共享鎖,如果是共享鎖,則直接喚醒
if (s == null || s.isShared())
// 喚醒節點,喚醒部分詳解
doReleaseShared();
}
}
4,嘗試釋放鎖
* tryReleaseShared(int arg):該方法依舊在子類中實現
protected boolean tryReleaseShared(int arg) {
throw new UnsupportedOperationException();
}
5,釋放鎖及釋放後下一個節點喚醒
* releaseShared(int arg):釋放共享鎖
public final boolean releaseShared(int arg) {
// 嘗試釋放共享鎖
if (tryReleaseShared(arg)) {
// 共享鎖釋放
doReleaseShared();
return true;
}
return false;
}
* doReleaseShared():喚醒下一個共享鎖節點
private void doReleaseShared() {
// 自旋進行喚醒
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
// 直接喚醒下一個節點,
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
6,喚醒後的線程繼續執行代碼
* doAcquireShared(int arg):線程被喚醒後,繼續自旋獲取鎖,獲取鎖成功後進行後續節點處理
private void doAcquireShared(int arg) {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head) {
// 自選獲取鎖
int r = tryAcquireShared(arg);
if (r >= 0) {
// 獲取鎖成功,設置頭節點,並喚醒後續節點
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}