Java并發(fā)編程——AQS源碼解析
- 什么是AQS
- AQS有什么用
- AQS實(shí)現(xiàn)方式
一、AQS是什么仿畸?
AQS是一個(gè)基于先進(jìn)先出(FIFO)等待隊(duì)列的實(shí)現(xiàn)阻塞鎖和同步器的框架。AQS通過一個(gè)volatile int state變量來保存鎖的狀態(tài)谢肾。子類必須通過:
- getState():獲取當(dāng)前的同步狀態(tài)
- setState(int newState):設(shè)置當(dāng)前同步狀態(tài)
- compareAndSetState(int expect,int update):使用CAS設(shè)置當(dāng)前狀態(tài)浦马,該方法能夠保證狀態(tài)設(shè)置的原子性。
三個(gè)方法修改獲取鎖的狀態(tài)值state殊校。
CAS (compare and swap) 比較并交換晴玖,就是將內(nèi)存值與預(yù)期值進(jìn)行比較,如果相等才將新值替換到內(nèi)存中为流,并返回true表示操作成功呕屎;如果不相等,則直接返回false表示操作失敗敬察。CAS操作大多都是靠CPU原語來實(shí)現(xiàn)秀睛。CAS操作經(jīng)常被用來實(shí)現(xiàn)無鎖數(shù)據(jù)結(jié)構(gòu),在java.util.concurrent包中就有很多這樣的數(shù)據(jù)結(jié)構(gòu):ConcurrentLinkedQueue莲祸、ConcurrentLinedDeque蹂安、ConcurrentHashMap、ConcurrentSkipListMap锐帜、ConcurrentSkipListSet田盈。
1、AQS支持的鎖的類別
AQS支持獨(dú)占鎖和共享鎖兩種缴阎。
- 獨(dú)占鎖:鎖在一個(gè)時(shí)間點(diǎn)只能被一個(gè)線程占有允瞧。根據(jù)鎖的獲取機(jī)制,又分為“公平鎖”和“非公平鎖”蛮拔。等待隊(duì)列中按照FIFO的原則獲取鎖述暂,等待時(shí)間越長的線程越先獲取到鎖,這就是公平的獲取鎖建炫,即公平鎖畦韭。而非公平鎖,線程獲取的鎖的時(shí)候肛跌,無視等待隊(duì)列直接獲取鎖艺配。ReentrantLock和ReentrantReadWriteLock.Writelock是獨(dú)占鎖据过。
- 共享鎖:同一個(gè)時(shí)候能夠被多個(gè)線程獲取的鎖,能被共享的鎖妒挎。JUC包中ReentrantReadWriteLock.ReadLock绳锅,CyclicBarrier,CountDownLatch和Semaphore都是共享鎖酝掩。
2鳞芙、基于AQS實(shí)現(xiàn)鎖
AQS中沒有實(shí)現(xiàn)任何的同步接口,所以一般子類通過繼承AQS以內(nèi)部類的形式實(shí)現(xiàn)鎖機(jī)制期虾。一般通過繼承AQS類實(shí)現(xiàn)同步器原朝,通過getState、setState镶苞、compareAndSetState來監(jiān)測狀態(tài)喳坠,并重寫以下方法:
- tryAcquire():獨(dú)占方式。嘗試獲取資源茂蚓,成功則返回true壕鹉,失敗則返回false。
- tryRelease():獨(dú)占方式聋涨。嘗試釋放資源晾浴,成功則返回true,失敗則返回false牍白。
- tryAcquireShared():共享方式脊凰。嘗試獲取資源。負(fù)數(shù)表示失斆取狸涌;0表示成功,但沒有剩余可用資源最岗;正數(shù)表示成功帕胆,且有剩余資源。
- tryReleaseShared():共享方式仑性。嘗試釋放資源惶楼,如果釋放后允許喚醒后續(xù)等待結(jié)點(diǎn)返回true,否則返回false诊杆。
- isHeldExclusively():該線程是否正在獨(dú)占資源。只有用到condition才需要去實(shí)現(xiàn)它何陆。
一般來說晨汹,自定義同步器要么是獨(dú)占方法,要么是共享方式贷盲,他們也只需實(shí)現(xiàn)tryAcquire-tryRelease淘这、tryAcquireShared-tryReleaseShared中的一種即可剥扣。但AQS也支持自定義同步器同時(shí)實(shí)現(xiàn)獨(dú)占和共享兩種方式,如ReentrantReadWriteLock铝穷。
3钠怯、AQS同步器的存儲(chǔ)結(jié)構(gòu)
二、AQS源碼解析
1曙聂、存儲(chǔ)節(jié)點(diǎn)Node
我們一直在說AQS是基于FIFO隊(duì)列的存儲(chǔ)結(jié)構(gòu)晦炊,它是以內(nèi)部類Node節(jié)點(diǎn)的形式進(jìn)行存儲(chǔ)。這個(gè)等待隊(duì)列是CLH同步隊(duì)列宁脊。
static final class Node {
/** 共享節(jié)點(diǎn)模式下的節(jié)點(diǎn) */
static final Node SHARED = new Node();
/** 獨(dú)占模式下的節(jié)點(diǎn) */
static final Node EXCLUSIVE = null;
/** 取消狀態(tài) */
static final int CANCELLED = 1;
/** 后繼節(jié)點(diǎn)的線程處于等待狀態(tài)断国,而當(dāng)前節(jié)點(diǎn)的線程如果釋放了同步狀態(tài)或者被取消,將會(huì)通知后繼節(jié)點(diǎn)榆苞,使后繼節(jié)點(diǎn)的線程得以運(yùn)行 */
static final int SIGNAL = -1;
/** waitStatus value to indicate thread is waiting on condition */
static final int CONDITION = -2;
/**
* 下一次共享式同步狀態(tài)獲取將會(huì)無條件地傳播下去
*/
static final int PROPAGATE = -3;
/**
* Status field, taking on only the values:
* SIGNAL: The successor of this node is (or will soon be)
* blocked (via park), so the current node must
* unpark its successor when it releases or
* cancels. To avoid races, acquire methods must
* first indicate they need a signal,
* then retry the atomic acquire, and then,
* on failure, block.
* CANCELLED: This node is cancelled due to timeout or interrupt.
* Nodes never leave this state. In particular,
* a thread with cancelled node never again blocks.
* CONDITION: This node is currently on a condition queue.
* It will not be used as a sync queue node
* until transferred, at which time the status
* will be set to 0. (Use of this value here has
* nothing to do with the other uses of the
* field, but simplifies mechanics.)
* PROPAGATE: A releaseShared should be propagated to other
* nodes. This is set (for head node only) in
* doReleaseShared to ensure propagation
* continues, even if other operations have
* since intervened.
* 0: None of the above
*
* The values are arranged numerically to simplify use.
* Non-negative values mean that a node doesn't need to
* signal. So, most code doesn't need to check for particular
* values, just for sign.
*
* The field is initialized to 0 for normal sync nodes, and
* CONDITION for condition nodes. It is modified using CAS
* (or when possible, unconditional volatile writes).
*/
volatile int waitStatus;
/**
* 前驅(qū)節(jié)點(diǎn)
*/
volatile Node prev;
/**
* 后驅(qū)節(jié)點(diǎn)
*/
volatile Node next;
/**
* 獲取同步狀態(tài)的線程
*/
volatile Thread thread;
/**
* Link to next node waiting on condition, or the special
* value SHARED. Because condition queues are accessed only
* when holding in exclusive mode, we just need a simple
* linked queue to hold nodes while they are waiting on
* conditions. They are then transferred to the queue to
* re-acquire. And because conditions can only be exclusive,
* we save a field by using special value to indicate shared
* mode.
*/
Node nextWaiter;
/**
* Returns true if node is waiting in shared mode.
*/
final boolean isShared() {
return nextWaiter == SHARED;
}
/**
* Returns previous node, or throws NullPointerException if null.
* Use when predecessor cannot be null. The null check could
* be elided, but is present to help the VM.
*
* @return the predecessor of this node
*/
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
Node() { // Used to establish initial head or SHARED marker
}
Node(Thread thread, Node mode) { // Used by addWaiter
this.nextWaiter = mode;
this.thread = thread;
}
Node(Thread thread, int waitStatus) { // Used by Condition
this.waitStatus = waitStatus;
this.thread = thread;
}
}
在Node內(nèi)部類中稳衬,聲明了pre、next節(jié)點(diǎn)用于隊(duì)列的連接坐漏,同時(shí)保存了waitStatus狀態(tài)薄疚。
2、AQS源碼解析
在面向?qū)ο蟮氖澜缰猩蘖眨胍私庖粋€(gè)類有什么特點(diǎn)输涕,就要看它的屬性。通過查看源碼慨畸,我們看到AQS類包含:
/**
* Head of the wait queue, lazily initialized. Except for
* initialization, it is modified only via method setHead. Note:
* If head exists, its waitStatus is guaranteed not to be
* CANCELLED.
*/
private transient volatile Node head;
/**
* Tail of the wait queue, lazily initialized. Modified only via
* method enq to add new wait node.
*/
private transient volatile Node tail;
/**
* The synchronization state.
*/
private volatile int state;
- 前驅(qū)頭節(jié)點(diǎn)-head
- 后驅(qū)尾節(jié)點(diǎn)-tail
- 同步器狀態(tài)-state
AQS基于FIFO隊(duì)列莱坎,接下來就依照acquire-release、acquireShared-releaseShared的次序來分析入隊(duì)和出隊(duì)寸士。
1檐什、acquire(int)
此方法是獨(dú)占模式下線程獲取共享資源的頂層入口。如果獲取到資源成功弱卡,線程直接返回乃正,否則進(jìn)入等待隊(duì)列,直到獲取到資源為止婶博,且整個(gè)過程忽略中斷的影響瓮具。這也正是lock()的語義,當(dāng)然不僅僅只限于lock()凡人。獲取到資源后名党,線程就可以去執(zhí)行其臨界區(qū)代碼了。下面是acquire源碼:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
在acquire方法中調(diào)用了tryAcquire()挠轴、acquireQueued()传睹、addWaiter()三個(gè)方法。
首先通過tryAcquire方法嘗試申請獨(dú)占鎖岸晦。如果獲取成功則返回true欧啤,否則返回false睛藻。
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
呦西,源碼中直接throw一個(gè)異常邢隧。結(jié)合我們前面自定義鎖的知識(shí)店印,AQS只是一個(gè)框架,具體資源獲取和釋放方式交由自定義同步器實(shí)現(xiàn)倒慧。AQS這里只定義了一個(gè)接口庶香,具體資源的獲取交由自定義同步器去實(shí)現(xiàn)了(通過state的get/set/CAS)V粝浴!!至于能不能重入卑雁,能不能加塞米酬,那就看具體的自定義同步器怎么去設(shè)計(jì)了F锿琛v搿!當(dāng)然盹牧,自定義同步器在進(jìn)行資源訪問時(shí)要考慮線程安全的影響俩垃。
這里之所以沒有定義成abstract,是因?yàn)楠?dú)占模式下只用實(shí)現(xiàn)tryAcquire-tryRelease汰寓,而共享模式下只用實(shí)現(xiàn)tryAcquireShared-tryReleaseShared口柳。如果都定義成abstract,那么每個(gè)模式也要去實(shí)現(xiàn)另一模式下的接口有滑。說到底跃闹,Doug Lea還是站在咱們開發(fā)者的角度,盡量減少不必要的工作量毛好。
接著就是addWaiter()方法用于將當(dāng)前線程添加到等待隊(duì)列的隊(duì)尾望艺,并返回當(dāng)前線程所在的節(jié)點(diǎn)。
private Node addWaiter(Node mode) {
//以給定的Node節(jié)點(diǎn)模式構(gòu)建當(dāng)前線程的Node節(jié)點(diǎn)肌访,在acquire方法中傳入的是EXCLUSIVE獨(dú)占式節(jié)點(diǎn)
Node node = new Node(Thread.currentThread(), mode);
// 將尾節(jié)點(diǎn)進(jìn)行保存
Node pred = tail;
if (pred != null) {//如果尾節(jié)點(diǎn)不為null
//將尾節(jié)點(diǎn)設(shè)置尾新節(jié)點(diǎn)的prev節(jié)點(diǎn)
node.prev = pred;
if (compareAndSetTail(pred, node)) {//通過CAS保證找默,確保節(jié)點(diǎn)能夠被線程安全的添加
//將當(dāng)前界定指向前驅(qū)的next節(jié)點(diǎn)
pred.next = node;
return node;
}
}
//如果尾節(jié)點(diǎn)為null,則通過enq進(jìn)行入隊(duì)
enq(node);
return node;
}
//同步器通過死循環(huán)的方式來保證節(jié)點(diǎn)的正確添加吼驶,在“死循環(huán)” 中通過CAS將節(jié)點(diǎn)設(shè)置成為尾節(jié)點(diǎn)之后惩激,
//當(dāng)前線程才能從該方法中返回,否則當(dāng)前線程不斷的嘗試設(shè)置蟹演。
private Node enq(final Node node) {
//CAS"自旋"风钻,直到成功加入隊(duì)尾
for (;;) {
//尾節(jié)點(diǎn)臨時(shí)存儲(chǔ)
Node t = tail;
if (t == null) { // Must initialize
//如果tail為null,則將
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
在addWaiter(Node node)方法中轨帜,將當(dāng)前線程節(jié)點(diǎn)添加到等待隊(duì)列中魄咕。
acquireQueued在隊(duì)列中的線程獲取鎖
/**
* Acquires in exclusive uninterruptible mode for thread already in
* queue. Used by condition wait methods as well as acquire.
*
* @param node the node
* @param arg the acquire argument
* @return {@code true} if interrupted while waiting
*
* acquireQueued方法當(dāng)前線程在死循環(huán)中獲取同步狀態(tài),而只有前驅(qū)節(jié)點(diǎn)是頭節(jié)點(diǎn)才能嘗試獲取同步狀態(tài)(鎖)( p == head && tryAcquire(arg))
* 原因是:1.頭結(jié)點(diǎn)是成功獲取同步狀態(tài)(鎖)的節(jié)點(diǎn)蚌父,而頭節(jié)點(diǎn)的線程釋放了同步狀態(tài)以后哮兰,將會(huì)喚醒其后繼節(jié)點(diǎn),后繼節(jié)點(diǎn)的線程被喚醒后要檢查自己的前驅(qū)節(jié)點(diǎn)是否為頭結(jié)點(diǎn)苟弛。
* 2.維護(hù)同步隊(duì)列的FIFO原則喝滞,節(jié)點(diǎn)進(jìn)入同步隊(duì)列之后,就進(jìn)入了一個(gè)自旋的過程膏秫,每個(gè)節(jié)點(diǎn)(或者說是每個(gè)線程)都在自省的觀察右遭。
*
*/
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
//死循環(huán)檢查(自旋檢查)當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)是否為頭結(jié)點(diǎn),才能獲取鎖
for (;;) {
// 獲取節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {//節(jié)點(diǎn)中的線程循環(huán)的檢查缤削,自己的前驅(qū)節(jié)點(diǎn)是否為頭節(jié)點(diǎn)
//將當(dāng)前節(jié)點(diǎn)設(shè)置為頭結(jié)點(diǎn)窘哈,移除之前的頭節(jié)點(diǎn)
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
// 否則檢查前一個(gè)節(jié)點(diǎn)的狀態(tài),看當(dāng)前獲取鎖失敗的線程是否要掛起
if (shouldParkAfterFailedAcquire(p, node) &&
//如果需要掛起亭敢,借助JUC包下面的LockSupport類的靜態(tài)方法park掛起當(dāng)前線程滚婉,直到被喚醒
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
//如果有異常
if (failed)
//取消請求,將當(dāng)前節(jié)點(diǎn)從隊(duì)列中移除
cancelAcquire(node);
}
}
通過addWaiter方法添加到等待隊(duì)列中后帅刀,在通過acquireQueued方法進(jìn)行鎖的獲取
函數(shù)流程:
- tryAcquire()嘗試直接去獲取資源让腹,如果成功則直接返回;
- addWaiter()將該線程加入等待隊(duì)列的尾部扣溺,并標(biāo)記為獨(dú)占模式骇窍;
- acquireQueued()使線程在等待隊(duì)列中獲取資源,一直獲取到資源后才返回锥余。如果在整個(gè)等待過程中被中斷過腹纳,則返回true,否則返回false驱犹。
- 如果線程在等待過程中被中斷過嘲恍,它是不響應(yīng)的。只是獲取資源后才再進(jìn)行自我中斷selfInterrupt()着绷,將中斷補(bǔ)上蛔钙。
獨(dú)占式鎖獲取流程
調(diào)用同步器的acquire(int arg)方法可以獲取同步狀態(tài),該方法對中斷不敏感荠医,即線程獲取同步狀態(tài)失敗后進(jìn)入同步隊(duì)列吁脱,后續(xù)對線程進(jìn)行中斷操作時(shí),線程不會(huì)從同步隊(duì)列中移除彬向。獲取流程:
- 當(dāng)前線程通過tryAcquire()方法嘗試獲取鎖兼贡,成功則直接返回,失敗則進(jìn)入隊(duì)列排隊(duì)等待娃胆,通過CAS獲取同步狀態(tài)遍希。
- 如果嘗試獲取鎖失敗的話,構(gòu)造同步節(jié)點(diǎn)(獨(dú)占式的Node.EXCLUSIVE)里烦,通過addWaiter(Node node,int args)方法,將節(jié)點(diǎn)加入到同步隊(duì)列的隊(duì)列尾部凿蒜。
- 最后調(diào)用acquireQueued(final Node node, int args)方法禁谦,使該節(jié)點(diǎn)以死循環(huán)的方式獲取同步狀態(tài),如果獲取不到废封,則阻塞節(jié)點(diǎn)中的線程州泊。acquireQueued方法當(dāng)前線程在死循環(huán)中獲取同步狀態(tài),而只有前驅(qū)節(jié)點(diǎn)是頭節(jié)點(diǎn)的時(shí)候才能嘗試獲取鎖(同步狀態(tài))( p == head && tryAcquire(arg))漂洋。
2遥皂、release(int):獨(dú)占鎖的釋放
在AQS中通過release方法進(jìn)行鎖的釋放。
public final boolean release(int arg) {
//調(diào)用tryRelease方法釋放
if (tryRelease(arg)) {//如果釋放成功
Node h = head;
//如果頭節(jié)點(diǎn)不為null刽漂,并且頭結(jié)點(diǎn)的waitStatus值不為0演训,即有狀態(tài)
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
//沒有釋放成功返回false
return false;
}
// tryRelease() 嘗試釋放當(dāng)前線程的同步狀態(tài)(鎖)
protected final boolean tryRelease(int releases) {
//c為釋放后的同步狀態(tài)
int c = getState() - releases;
//判斷當(dāng)前釋放鎖的線程是否為獲取到鎖(同步狀態(tài))的線程,不是拋出異常(非法監(jiān)視器狀態(tài)異常)
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//如果鎖(同步狀態(tài))已經(jīng)被當(dāng)前線程徹底釋放贝咙,則設(shè)置鎖的持有者為null样悟,同步狀態(tài)(鎖)變的可獲取
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
Node s = node.next;
//從隊(duì)列尾部開始往前去找最前面的一個(gè)waitStatus小于0的節(jié)點(diǎn)。
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
//喚醒后繼節(jié)點(diǎn)對應(yīng)的線程
if (s != null)
LockSupport.unpark(s.thread);
}
release()是獨(dú)占模式下線程釋放共享資源的頂層入口颈畸。它會(huì)釋放指定量的資源乌奇,如果徹底釋放了(即state=0),它會(huì)喚醒等待隊(duì)列里的其他線程來獲取資源。
3眯娱、acquireShared(int)
此方法是共享模式下線程獲取共享資源的頂層入口礁苗。它會(huì)獲取指定量的資源,獲取成功則直接返回徙缴,獲取失敗則進(jìn)入等待隊(duì)列试伙,直到獲取到資源為止,整個(gè)過程忽略中斷于样。下面是acquireShared()的源碼:
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
private void doAcquireShared(int arg) {
final Node node = addWaiter(Node.SHARED);//加入隊(duì)列尾部
boolean failed = true;//是否成功標(biāo)志
try {
boolean interrupted = false;//等待過程中是否被中斷過的標(biāo)志
for (;;) {
final Node p = node.predecessor();//前驅(qū)
if (p == head) {//如果到head的下一個(gè)疏叨,因?yàn)閔ead是拿到資源的線程,此時(shí)node被喚醒穿剖,很可能是head用完資源來喚醒自己的
int r = tryAcquireShared(arg);//嘗試獲取資源
if (r >= 0) {//成功
setHeadAndPropagate(node, r);//將head指向自己蚤蔓,還有剩余資源可以再喚醒之后的線程
p.next = null; // help GC
if (interrupted)//如果等待過程中被打斷過,此時(shí)將中斷補(bǔ)上糊余。
selfInterrupt();
failed = false;
return;
}
}
//判斷狀態(tài)秀又,尋找安全點(diǎn),進(jìn)入waiting狀態(tài)贬芥,等著被unpark()或interrupt()
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
這里tryAcquireShared()依然需要自定義同步器去實(shí)現(xiàn)吐辙。但是AQS已經(jīng)把其返回值的語義定義好了:負(fù)值代表獲取失敗蘸劈;0代表獲取成功昏苏,但沒有剩余資源;正數(shù)表示獲取成功,還有剩余資源贤惯,其他線程還可以去獲取洼专。所以這里acquireShared()的流程就是:
- tryAcquireShared()嘗試獲取資源,成功則直接返回救巷;
- 失敗則通過doAcquireShared()進(jìn)入等待隊(duì)列park()壶熏,直到被unpark()/interrupt()并成功獲取到資源才返回句柠。整個(gè)等待過程也是忽略中斷的浦译。
doAcquireShared(int)此方法用于將當(dāng)前線程加入等待隊(duì)列尾部休息,直到其他線程釋放資源喚醒自己溯职,自己成功拿到相應(yīng)量的資源后才返回精盅。
4、releaseShared()
releaseShared()是共享模式下線程釋放共享資源的頂層入口谜酒。它會(huì)釋放指定量的資源叹俏,如果成功釋放且允許喚醒等待線程,它會(huì)喚醒等待隊(duì)列里的其他線程來獲取資源僻族。
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {//嘗試釋放資源
doReleaseShared();//喚醒后繼結(jié)點(diǎn)
return true;
}
return false;
}
此方法的流程也比較簡單粘驰,一句話:釋放掉資源后,喚醒后繼述么。跟獨(dú)占模式下的release()相似蝌数,但有一點(diǎn)稍微需要注意:獨(dú)占模式下的tryRelease()在完全釋放掉資源(state=0)后,才會(huì)返回true去喚醒其他線程度秘,這主要是基于獨(dú)占下可重入的考量顶伞;而共享模式下的releaseShared()則沒有這種要求,共享模式實(shí)質(zhì)就是控制一定量的線程并發(fā)執(zhí)行剑梳,那么擁有資源的線程在釋放掉部分資源時(shí)就可以喚醒后繼等待結(jié)點(diǎn)唆貌。例如,資源總量是13垢乙,A(5)和B(7)分別獲取到資源并發(fā)運(yùn)行锨咙,C(4)來時(shí)只剩1個(gè)資源就需要等待。A在運(yùn)行過程中釋放掉2個(gè)資源量追逮,然后tryReleaseShared(2)返回true喚醒C酪刀,C一看只有3個(gè)仍不夠繼續(xù)等待;隨后B又釋放2個(gè)羊壹,tryReleaseShared(2)返回true喚醒C蓖宦,C一看有5個(gè)夠自己用了,然后C就可以跟A和B一起運(yùn)行油猫。而ReentrantReadWriteLock讀鎖的tryReleaseShared()只有在完全釋放掉資源(state=0)才返回true稠茂,所以自定義同步器可以根據(jù)需要決定tryReleaseShared()的返回值。
總結(jié)
本節(jié)中針對AQS進(jìn)行了簡單的學(xué)習(xí),總結(jié)中參照了以下文章睬关,推薦大家去閱讀下诱担。
- JUC回顧之-AQS同步器的實(shí)現(xiàn)原理:https://www.cnblogs.com/200911/p/60313
- 50.html
- JDK源碼之AQS源碼剖析:http://www.cnblogs.com/showing/p/6858410.html
- Java并發(fā)之AQS詳解:https://www.cnblogs.com/waterystone/p/4920797.html