10分鐘從源碼級別搞懂AQS(AbstractQueuedSynchronizer)
前言
上篇文章15000字、6個代碼案例、5個原理圖讓你徹底搞懂Synchronized有說到synchronized由object monitor實現(xiàn)的
object monitor中由cxq棧和entry list來實現(xiàn)阻塞隊列,wait set實現(xiàn)等待隊列,從而實現(xiàn)synchronized的等待/通知模式
而JDK中的JUC并發(fā)包也通過類似的阻塞隊列和等待隊列實現(xiàn)等待/通知模式
這篇文章就來講講JUC的基石AQS(AbstractQueuedSynchronizer)
需要了解的前置知識:CAS、volatile
如果不了解CAS可以看上篇講述synchronized的文章(鏈接在上面)
如果不了解volatile可以看這篇文章 5個案例和流程圖讓你從0到1搞懂volatile關鍵字
本篇文章以AQS為中心,深入淺出描述AQS中的數(shù)據(jù)結構、設計以及獲取、釋放同步狀態(tài)的源碼流程、Condition等
觀看本文大約需要10分鐘,可以帶著幾個問題去觀看
- 什么是AQS,它是干啥用的?
- AQS是使用什么數(shù)據(jù)結構實現(xiàn)的?
- AQS獲取/釋放同步狀態(tài)是如何實現(xiàn)的?
- AQS除了具有synchronized的功能還擁有什么其他特性?
- AQS如何去實現(xiàn)非公平鎖、公平鎖?
- 什么是Condition?它跟AQS是什么關系?
AQS數(shù)據(jù)結構
什么是AQS呢?
AQS是一個同步隊列(阻塞隊列),是并發(fā)包中的基礎,很多并發(fā)包中的同步組件底層都使用AQS來實現(xiàn),比如:ReentrantLock、讀寫鎖、信號量等等...
AQS有三個重要的字段,分別是: head 頭節(jié)點、tail 尾節(jié)點、state 同步狀態(tài)
public abstract class AbstractQueuedSynchronizer
? ?extends AbstractOwnableSynchronizer
? ?implements java.io.Serializable {
? ?//頭節(jié)點
? ?private transient volatile Node head;
//尾節(jié)點
? ?private transient volatile Node tail;
? ?//同步狀態(tài)
? ?private volatile int state; ?
} ? ?
頭尾節(jié)點很好理解,因為AQS本身就是個雙向鏈表,那么state同步狀態(tài)是什么?
AQS中使用同步狀態(tài)表示資源,然后使用CAS來獲取/釋放資源,比如設置資源為1,一個線程來嘗試獲取資源,由于同步狀態(tài)目前為1,于是該線程CAS替換同步狀態(tài)為0,成功后表示獲取到資源,之后其他線程再來獲取資源就無法獲取了(狀態(tài)為0),直到獲取資源的線程來釋放資源
上述獲取/釋放資源也可以理解成獲取/釋放鎖
同時三個字段都被volatile修飾,用volatile來保證內存可見性,防止其他線程修改這些數(shù)據(jù)時當前線程無法感知
通過上面的描述,我們可以知道AQS大概長這樣
當某個線程獲取資源失敗時,會被構建成節(jié)點加入AQS中
節(jié)點Node是AQS中的內部類,Node中有些重要的字段一起來看看
static final class Node {
? ?//節(jié)點狀態(tài)
? ? ? ?volatile int waitStatus;
? ?
? //前驅節(jié)點
? ? ? ?volatile Node prev;
?
? ? //后繼節(jié)點
? ? ? ?volatile Node next;
? //當前節(jié)點所代表的線程
? ? ? ?volatile Thread thread;
?
? //等待隊列使用時的后繼節(jié)點指針
? ? ? ?Node nextWaiter;
}
prev、next、thread應該都好理解
AQS同步隊列和等待隊列都使用這種節(jié)點,當?shù)却犃泄?jié)點被喚醒出隊時,方便加入同步隊列
nextWaiter就是用于節(jié)點在等待隊列中指向下一個節(jié)點
waitStatus表示節(jié)點的狀態(tài)
狀態(tài) | 說明 |
---|---|
INITIAL | 0 初始狀態(tài) |
CANCELLED | 1 該節(jié)點對應的線程取消調度 |
SIGNAL | -1 該節(jié)點對應的線程阻塞,等待喚醒競爭資源 |
CONDITION | -2 該節(jié)點在等待(條件)隊列中,等待喚醒后從等待隊列出隊進入同步隊列競爭 |
PROPAGATE | -3 共享情況下,會喚醒后續(xù)所有共享節(jié)點 |
不太理解狀態(tài)不要緊,我們后文遇到再說
經(jīng)過上面的描述,節(jié)點大概是長成這樣的
AQS中還有另外一個內部類ConditionObject
用于實現(xiàn)等待隊列/條件隊列,我們后文再來說說
AQS中可以分為獨占、共享模式,其中這兩種模式下還可以支持響應中斷、納秒級別超時
獨占模式可以理解為同一時間只有一個線程能夠獲取同步狀態(tài)
共享模式可以理解為可以有多個線程能夠獲取同步狀態(tài),方法中常用shared
標識
方法中常用acquire
標識獲取同步狀態(tài),release
標識釋放同步狀態(tài)
這些方法都是模板方法,規(guī)定流程,將具體的實現(xiàn)留給實現(xiàn)類去做(比如獲取同步狀態(tài),該如何獲取交給實現(xiàn)類去實現(xiàn))
獨占式
獨占式實際就是時刻上只允許一個線程獨占該資源,多線程競爭情況下也只能有一個線程獲取同步狀態(tài)成功
獲取同步狀態(tài)
不響應中斷的獨占獲取和響應中斷、超時的類似,我們以acquire
為例查看源碼
public final void acquire(int arg) {
? ? ? ?if (!tryAcquire(arg) &&
? ? ? ? ? ?acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
? ? ? ? ? ?selfInterrupt();
? }
tryAcquire
方法用于嘗試獲取同步狀態(tài),參數(shù)arg表示獲取多少同步狀態(tài),獲取成功返回true 則會退出方法,留給實現(xiàn)類去實現(xiàn)
addWaiter
addWaiter(Node.EXCLUSIVE) 構建獨占式節(jié)點,并用CAS+失敗重試的方式加入AQS的末尾
private Node addWaiter(Node mode) {
? ? ? ?//構建節(jié)點
? ? ? ?Node node = new Node(Thread.currentThread(), mode);
? ? ? ?//尾節(jié)點不為空則CAS替換尾節(jié)點
? ? ? ?Node pred = tail;
? ? ? ?if (pred != null) {
? ? ? ? ? ?node.prev = pred;
? ? ? ? ? ?if (compareAndSetTail(pred, node)) {
? ? ? ? ? ? ? ?pred.next = node;
? ? ? ? ? ? ? ?return node;
? ? ? ? ? }
? ? ? }
? ? ? ?//尾節(jié)點為空或則CAS失敗執(zhí)行enq
? ? ? ?enq(node);
? ? ? ?return node;
? }
? ?private Node enq(final Node node) {
? ? ? ?//失敗重試
? ? ? ?for (;;) {
? ? ? ? ? ?Node t = tail;
? ? ? ? ? ?//沒有尾節(jié)點 則CAS設置頭節(jié)點(頭尾節(jié)點為一個節(jié)點),否則CAS設置尾節(jié)點
? ? ? ? ? ?if (t == null) { // Must initialize
? ? ? ? ? ? ? ?if (compareAndSetHead(new Node()))
? ? ? ? ? ? ? ? ? ?tail = head;
? ? ? ? ? } else {
? ? ? ? ? ? ? ?node.prev = t;
? ? ? ? ? ? ? ?if (compareAndSetTail(t, node)) {
? ? ? ? ? ? ? ? ? ?t.next = node;
? ? ? ? ? ? ? ? ? ?return t;
? ? ? ? ? ? ? }
? ? ? ? ? }
? ? ? }
? }
enq
方法主要以自旋(中途不會進入等待模式)去CAS設置尾節(jié)點,如果AQS中沒有節(jié)點則頭尾節(jié)點為同一節(jié)點
由于添加到尾節(jié)點存在競爭,因此需要用CAS去替換尾節(jié)點
acquireQueued
acquireQueued
方法主要用于AQS隊列中的節(jié)點來自旋獲取同步狀態(tài),在這個自旋中并不是一直執(zhí)行的,而是會被park進入等待
final boolean acquireQueued(final Node node, int arg) {
? ?//記錄是否失敗
? ?boolean failed = true;
? ?try {
? ? ? ?//記錄是否中斷過
? ? ? ?boolean interrupted = false;
? ? ? ?//失敗重試
? ? ? ?for (;;) {
? ? ? ? ? ?//p 前驅節(jié)點
? ? ? ? ? ?final Node p = node.predecessor();
? ? ? ? ? ?//如果前驅節(jié)點為頭節(jié)點,并嘗試獲取同步狀態(tài)成功則返回
? ? ? ? ? ?if (p == head && tryAcquire(arg)) {
? ? ? ? ? ? ? ?//設置頭節(jié)點
? ? ? ? ? ? ? ?setHead(node);
? ? ? ? ? ? ? ?p.next = null; // help GC
? ? ? ? ? ? ? ?failed = false;
? ? ? ? ? ? ? ?return interrupted;
? ? ? ? ? }
? ? ? ? ? ?//失敗則設置下標記然后進入等待檢查中斷
? ? ? ? ? ?if (shouldParkAfterFailedAcquire(p, node) &&
? ? ? ? ? ? ? ?parkAndCheckInterrupt())
? ? ? ? ? ? ? ?interrupted = true;
? ? ? }
? } finally {
? ? ? ?//如果失敗則取消獲取
? ? ? ?if (failed)
? ? ? ? ? ?cancelAcquire(node);
? }
}
在嘗試獲取同步狀態(tài)前有個條件p == head && tryAcquire(arg)
:前驅節(jié)點是頭節(jié)點
因此AQS中的節(jié)點獲取狀態(tài)是FIFO的
但即使?jié)M足前驅節(jié)點是頭節(jié)點,并不一定就能獲取同步狀態(tài)成功,因為還未加入AQS的線程也可能嘗試獲取同步狀態(tài),以此來實現(xiàn)非公平鎖
那如何實現(xiàn)公平鎖呢?
在嘗試獲取同步狀態(tài)前都加上這個條件就行了唄!
再來看看shouldParkAfterFailedAcquire
獲取同步狀態(tài)失敗后應該停放
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
? ?//前驅節(jié)點狀態(tài)
? ?int ws = pred.waitStatus;
? ?if (ws == Node.SIGNAL)
? ? ? ?//前驅節(jié)點狀態(tài)是SIGNAL 說明前驅釋放同步狀態(tài)回來喚醒 直接返回
? ? ? ?return true;
? ?if (ws > 0) {
? ? ? ?//如果前驅狀態(tài)大于0 說明被取消了,就一直往前找,找到?jīng)]被取消的節(jié)點
? ? ? ?do {
? ? ? ? ? ?node.prev = pred = pred.prev;
? ? ? } while (pred.waitStatus > 0);
? ? ? ?//排在沒被取消的節(jié)點后面
? ? ? ?pred.next = node;
? } else {
? ? ? ?//前驅沒被取消,而且狀態(tài)不是SIGNAL CAS將狀態(tài)更新為SIGNAL,釋放同步狀態(tài)要來喚醒
? ? ? ?compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
? }
? ?return false;
}
實際上是park前的一些準備
再來看看 parkAndCheckInterrupt
,用工具類進入等待狀態(tài),被喚醒后檢查是否中斷
private final boolean parkAndCheckInterrupt() {
? ? ? ?//線程進入等待狀態(tài)...
? ? ? ?LockSupport.park(this);
? ? ? ? //檢查是否中斷 (會清除中斷標記位)
? ? ? ?return Thread.interrupted();
}
在acquireQueued
的中如果未獲取同步狀態(tài)并且拋出異常,最終會執(zhí)行cancelAcquire
取消
當感知到中斷時返回true回去,來到第一層acquire
方法執(zhí)行selfInterrupt
方法,自己中斷線程
acquire流程圖:
- 先嘗試獲取同步狀態(tài)失敗則CAS+失敗重試添加到AQS末尾
- 前驅節(jié)點為頭節(jié)點且獲取同步狀態(tài)成功則返回,否則進入等待狀態(tài)等待喚醒,喚醒后重試
- 在2期間發(fā)生異常取消當前節(jié)點
釋放同步狀態(tài)
先進行釋放同步狀態(tài),成功后頭節(jié)點狀態(tài)不為0 喚醒下一個狀態(tài)不是被取消的節(jié)點
public final boolean release(int arg) {
? ?//釋放同步狀態(tài)
? ?if (tryRelease(arg)) {
? ? ? ?Node h = head;
? ? ? ?if (h != null && h.waitStatus != 0)
? ? ? ? ? ?//喚醒下一個狀態(tài)不大于0(大于0就是取消)的節(jié)點
? ? ? ? ? ?unparkSuccessor(h);
? ? ? ?return true;
? }
? ?return false;
}
響應中斷
acquireInterruptibly
用于響應中斷的獲取同步狀態(tài)
public final void acquireInterruptibly(int arg)
? ? ? ?throws InterruptedException {
? ?//查看是否被中斷,中斷拋出異常
? ?if (Thread.interrupted())
? ? ? ?throw new InterruptedException();
? ?if (!tryAcquire(arg))
? ? ? ?doAcquireInterruptibly(arg);
}
doAcquireInterruptibly
與原過程類似,就是在被喚醒后檢查到被中斷時拋出中斷異常
private void doAcquireInterruptibly(int arg)
? ? ? ?throws InterruptedException {
? ? ? ?final Node node = addWaiter(Node.EXCLUSIVE);
? ? ? ?boolean failed = true;
? ? ? ?try {
? ? ? ? ? ?for (;;) {
? ? ? ? ? ? ? ?final Node p = node.predecessor();
? ? ? ? ? ? ? ?if (p == head && tryAcquire(arg)) {
? ? ? ? ? ? ? ? ? ?setHead(node);
? ? ? ? ? ? ? ? ? ?p.next = null; // help GC
? ? ? ? ? ? ? ? ? ?failed = false;
? ? ? ? ? ? ? ? ? ?return;
? ? ? ? ? ? ? }
? ? ? ? ? ? ? ?if (shouldParkAfterFailedAcquire(p, node) &&
? ? ? ? ? ? ? ? ? ?parkAndCheckInterrupt())
? ? ? ? ? ? ? ? ? ?//被喚醒后檢查到被中斷時拋出中斷異常
? ? ? ? ? ? ? ? ? ?throw new InterruptedException();
? ? ? ? ? }
? ? ? } finally {
? ? ? ? ? ?if (failed)
? ? ? ? ? ? ? ?cancelAcquire(node);
? ? ? }
? }
響應中斷的獲取同步狀態(tài)被中斷時會直接拋出中斷異常,而不響應的是自己中斷
響應超時
響應超時的獲取同步狀態(tài)使用tryAcquireNanos
超時時間為納秒級別
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
? ? ? ?throws InterruptedException {
? ?if (Thread.interrupted())
? ? ? ?throw new InterruptedException();
? ?return tryAcquire(arg) ||
? ? ? ?doAcquireNanos(arg, nanosTimeout);
}
可以看出響應超時同時也會響應中斷
doAcquireNanos
也與原過程類似
private boolean doAcquireNanos(int arg, long nanosTimeout)
? ? ? ? ? ?throws InterruptedException {
? ? ? ?if (nanosTimeout <= 0L)
? ? ? ? ? ?return false;
? ? ? ?final long deadline = System.nanoTime() + nanosTimeout;
? ? ? ?final Node node = addWaiter(Node.EXCLUSIVE);
? ? ? ?boolean failed = true;
? ? ? ?try {
? ? ? ? ? ?for (;;) {
? ? ? ? ? ? ? ?final Node p = node.predecessor();
? ? ? ? ? ? ? ?if (p == head && tryAcquire(arg)) {
? ? ? ? ? ? ? ? ? ?setHead(node);
? ? ? ? ? ? ? ? ? ?p.next = null; // help GC
? ? ? ? ? ? ? ? ? ?failed = false;
? ? ? ? ? ? ? ? ? ?return true;
? ? ? ? ? ? ? }
? ? ? ? ? ? ? ?//還有多久超時
? ? ? ? ? ? ? ?nanosTimeout = deadline - System.nanoTime();
? ? ? ? ? ? ? ?if (nanosTimeout <= 0L)
? ? ? ? ? ? ? ? ? ?//已超時
? ? ? ? ? ? ? ? ? ?return false;
? ? ? ? ? ? ? ?if (shouldParkAfterFailedAcquire(p, node) &&
? ? ? ? ? ? ? ? ? ?//大于1ms
? ? ? ? ? ? ? ? ? ?nanosTimeout > spinForTimeoutThreshold)
? ? ? ? ? ? ? ? ? ?//超時等待
? ? ? ? ? ? ? ? ? ?LockSupport.parkNanos(this, nanosTimeout);
? ? ? ? ? ? ? ?//響應中斷
? ? ? ? ? ? ? ?if (Thread.interrupted())
? ? ? ? ? ? ? ? ? ?throw new InterruptedException();
? ? ? ? ? }
? ? ? } finally {
? ? ? ? ? ?if (failed)
? ? ? ? ? ? ? ?cancelAcquire(node);
? ? ? }
? }
響應超時在自旋期間會計算還有多久超時,如果大于1ms就等待對應的時間,否則就繼續(xù)自旋,同時響應中斷
共享
共享式就是允許多個線程同時獲取一定的資源,比如信號量、讀鎖就是用共享式實現(xiàn)的
其實共享式與獨占式流程類似,只是嘗試獲取同步狀態(tài)的實現(xiàn)不同
我們用個獲取同步狀態(tài)的方法來說明
共享式獲取同步狀態(tài)使用acquireShared
public final void acquireShared(int arg) {
? ?if (tryAcquireShared(arg) < 0)
? ? ? ?doAcquireShared(arg);
}
tryAcquireShared
嘗試獲取同步狀態(tài),參數(shù)arg表示獲取多少同步狀態(tài),返回剩余可獲取同步狀態(tài)的數(shù)量
如果剩余可獲取同步狀態(tài)數(shù)量小于0 說明 未獲取成功進入doAcquireShared
private void doAcquireShared(int arg) {
? ? ? ?//添加共享式節(jié)點
? ? ? ?final Node node = addWaiter(Node.SHARED);
? ? ? ?boolean failed = true;
? ? ? ?try {
? ? ? ? ? ?boolean interrupted = false;
? ? ? ? ? ?for (;;) {
? ? ? ? ? ? ? ?//獲取前驅節(jié)點
? ? ? ? ? ? ? ?final Node p = node.predecessor();
? ? ? ? ? ? ? ?if (p == head) {
? ? ? ? ? ? ? ? ? ?int r = tryAcquireShared(arg);
? ? ? ? ? ? ? ? ? ?if (r >= 0) {
? ? ? ? ? ? ? ? ? ? ? ?//如果前驅節(jié)點為頭節(jié)點 并且 獲取同步狀態(tài)成功 設置頭節(jié)點
? ? ? ? ? ? ? ? ? ? ? ?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);
? ? ? }
? }
響應中斷、超時等方法也與獨占式類似,只是有些設置細節(jié)不同
Condition
上文曾說過AQS充當阻塞(同步)隊列,Condition來充當?shù)却犃?/p>
AQS的內部類ConditionObject就是Condition的實現(xiàn),它充當?shù)却犃?,用字段記錄頭尾節(jié)點
public class ConditionObject implements Condition{
? ? ? ?//頭節(jié)點
? ? private transient Node firstWaiter;
? ? ? ?//尾節(jié)點
? ? ? ?private transient Node lastWaiter; ?
}
節(jié)點之間使用nextWait指向下一個節(jié)點,形成單向鏈表
同時提供await
系列方法來讓當前線程進入等待,signal
系列方法來喚醒
public final void await() throws InterruptedException {
? ? ? ? ? ?//響應中斷
? ? ? ? ? ?if (Thread.interrupted())
? ? ? ? ? ? ? ?throw new InterruptedException();
? ? ? ? ? ?//添加到末尾 不需要保證原子性,因為能指向await一定是獲取到同步資源的
? ? ? ? ? ?Node node = addConditionWaiter();
? ? ? ? ? ?//釋放獲取的同步狀態(tài)
? ? ? ? ? ?int savedState = fullyRelease(node);
? ? ? ? ? ?int interruptMode = 0;
? ? ? ? ? ?//不在同步隊列就park進入等待
? ? ? ? ? ?while (!isOnSyncQueue(node)) {
? ? ? ? ? ? ? ?LockSupport.park(this);
? ? ? ? ? ? ? ?if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
? ? ? ? ? ? ? ? ? ?break;
? ? ? ? ? }
? ? ? ? ? ?//被喚醒后自旋獲取同步狀態(tài)
? ? ? ? ? ?if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
? ? ? ? ? ? ? ?interruptMode = REINTERRUPT;
? ? ? ? ? ?//取消后清理
? ? ? ? ? ?if (node.nextWaiter != null) // clean up if cancelled
? ? ? ? ? ? ? ?unlinkCancelledWaiters();
? ? ? ? ? ?if (interruptMode != 0)
? ? ? ? ? ? ? ?reportInterruptAfterWait(interruptMode);
? ? ? }
await主要將節(jié)點添加到condition object末尾,釋放獲取的同步狀態(tài),進入等待,喚醒后自旋獲取同步狀態(tài)
signal的主要邏輯在transferForSignal中
final boolean transferForSignal(Node node) {
? ? ? ?//CAS修改節(jié)點狀態(tài) 失敗返回 變成取消
? ? ? ?if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
? ? ? ? ? ?return false;
? ? ? ?//加入AQS末尾
? ? ? ?Node p = enq(node);
? ? ? ?int ws = p.waitStatus;
? ? ? ?//CAS將節(jié)點狀態(tài)修改為SIGNAL 成功則喚醒節(jié)點
? ? ? ?if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
? ? ? ? ? ?LockSupport.unpark(node.thread);
? ? ? ?return true;
? }
signal 主要把狀態(tài)從-2condition 修改為 0(失敗則取消節(jié)點), 然后加入AQS的末尾,最后再將狀態(tài)該為-1 signal,成功則喚醒節(jié)點
為什么加入AQS末尾還是使用enq去CAS+失敗重試操作保證原子性呢?
因為ConditionObject允許有多個,也就一個AQS同步隊列可能對應多個Condition等待(條件)隊列
總結
本篇文章以AQS為核心,深入淺出的描述AQS實現(xiàn)的數(shù)據(jù)結構、設計思想、獲取/釋放同步資源的源碼級流程、Condition等
AQS使用頭尾節(jié)點來實現(xiàn)雙向隊列,提供同步狀態(tài)和獲取/釋放同步狀態(tài)的模板方法來實現(xiàn)阻塞(同步)隊列,并且這些字段使用volatile修飾,保證可見性與讀取的場景配合,不需要保證原子性,在寫的場景下常用CAS保證原子性
AQS與Condition使用相同類型的節(jié)點,在AQS中節(jié)點維護成雙向鏈表,在Condition中節(jié)點維護成單向鏈表,節(jié)點除了維護指向關系,還需要記錄對應線程和節(jié)點狀態(tài)
AQS分為獨占式和共享式,使用獨占式時只允許一個線程獲取同步狀態(tài),使用共享式時則允許多個線程獲取同步狀態(tài);其中還提供響應中斷、等待超時的類似方法
獲取同步狀態(tài):先嘗試獲取同步狀態(tài),如果失敗則CAS+失敗重試的方式將節(jié)點添加到AQS末尾,等待被前驅節(jié)點喚醒;只有當前驅節(jié)點為頭節(jié)點并且獲取同步狀態(tài)成功才返回,否則進入等待,被喚醒后繼續(xù)嘗試(自旋);在此期間如果發(fā)生異常,在拋出異常前會取消該節(jié)點
釋放同步狀態(tài):嘗試釋放同步狀態(tài),成功后喚醒后繼未被取消的節(jié)點
在獲取同步狀態(tài)時,被喚醒后會檢查中斷標識,如果是響應中斷的則會直接拋出中斷異常,不響應的則是在最外層自己中斷
響應超時時,在自旋獲取同步狀態(tài)期間會計時,如果距離超時小于1ms就不進入等待的自旋,大于則再等待對應時間
AQS充當阻塞隊列,Condition充當它的等待隊列來實現(xiàn)等待/通知模式,AQS的內部類ConditionObject在await時會加入Condition末尾并釋放同步狀態(tài)進入等待隊列,在被喚醒后自旋(失敗會進入等待)獲取同步狀態(tài);在single時會CAS的將condition頭節(jié)點并加入AQS尾部再去喚醒(因為一個AQS可能對應多個Condition因此要CAS保證原子性)
最后(不要白嫖,一鍵三連求求拉~)
本篇文章被收入專欄 由點到線,由線到面,深入淺出構建Java并發(fā)編程知識體系,感興趣的同學可以持續(xù)關注喔
本篇文章筆記以及案例被收入 gitee-StudyJava、 github-StudyJava 感興趣的同學可以stat下持續(xù)關注喔~
有什么問題可以在評論區(qū)交流,如果覺得菜菜寫的不錯,可以點贊、關注、收藏支持一下~
關注菜菜,分享更多干貨,公眾號:菜菜的后端私房菜文章來源:http://www.zghlxwxcb.cn/news/detail-692476.html
本文由博客一文多發(fā)平臺 OpenWrite 發(fā)布!文章來源地址http://www.zghlxwxcb.cn/news/detail-692476.html
到了這里,關于10分鐘從源碼級別搞懂AQS(AbstractQueuedSynchronizer)的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!