本篇文章對(duì)Java中的AbstractQueuedSynchronizer(AQS)進(jìn)行分析和學(xué)習(xí)厚掷。若有不正之處請(qǐng)多多諒解唆阿,并歡迎批評(píng)指正。
為敘述方便锐墙,下文都以AQS替代AbstractQueuedSynchronizer析校。
使用的Java版本
src git:(master) ? java -version
java version "1.8.0_201"
Java(TM) SE Runtime Environment (build 1.8.0_201-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.201-b09, mixed mode)
AQS是干什么的呢构罗?
下面是AQS類的部分介紹,咱也看不懂智玻,只能用百度翻譯一下哈哈遂唧,建議英文好的直接看源碼里的類注釋。
AQS提供一個(gè)框架吊奢,用于實(shí)現(xiàn)依賴先進(jìn)先出(FIFO)等待隊(duì)列的阻塞鎖和相關(guān)同步器(semaphores盖彭、events等)纹烹。對(duì)于大多數(shù)依賴單個(gè)原子{@code int}值來表示狀態(tài)的同步器來說,這個(gè)類是一個(gè)有用的基礎(chǔ)召边。子類必須定義改變這個(gè)狀態(tài)的受保護(hù)的方法铺呵,以及定義這個(gè)狀態(tài)在對(duì)象被獲取或釋放時(shí)的含義。鑒于這些隧熙,AQS類中的其他方法執(zhí)行所有排隊(duì)和阻塞機(jī)制片挂。
子類應(yīng)該被定義為非公共的內(nèi)部輔助類,用于實(shí)現(xiàn)其封閉類的同步屬性贞盯。AbstractQueuedSynchronizer類不實(shí)現(xiàn)任何同步接口音念。相反,它定義了例如{@link #acquireInterruptibly}這樣的方法躏敢,可以根據(jù)具體的鎖和相關(guān)的同步器來適當(dāng)?shù)卣{(diào)用闷愤,以實(shí)現(xiàn)它們的公共方法。
一句話:本篇文章只需要知道AQS可以用來實(shí)現(xiàn)鎖即可件余。
我們一般不會(huì)直接使用AQS讥脐,所以我們以ReentrantLock(可重入鎖)來引出AQS。明白了AQS就明白了ReentrantLock是如何獲取鎖以及釋放鎖的了啼器。
先說一下大致流程:
Java中的ReentrantLock的獲取鎖和釋放鎖是通過AQS來實(shí)現(xiàn)的旬渠。
AQS內(nèi)部維護(hù)了一個(gè)int類型的值來表示
同步狀態(tài)
和一個(gè)先進(jìn)先出(FIFO)的等待隊(duì)列
。
/**
* 同步狀態(tài)
*/
private volatile int state;
/**
* 等待隊(duì)列的head镀首,延遲初始化。除了初始化之外鼠次,head只能通過setHead方法來修改更哄。
* 注意,如果head存在可以保證head的waitStatus不是CANCELLED.
*/
private transient volatile Node head;
/**
* 等待隊(duì)列的尾腥寇,惰性初始成翩。只有在使用enq方法添加新的等待節(jié)點(diǎn)的時(shí)候修改。
*/
private transient volatile Node tail;
- 對(duì)于非公平鎖赦役,線程總是會(huì)先嘗試獲取鎖麻敌,如果獲取成功就直接執(zhí)行,如果獲取失敗會(huì)進(jìn)入等待隊(duì)列掂摔。進(jìn)入等待隊(duì)列中的線程會(huì)休眠术羔,等待被喚醒。
- 對(duì)于公平鎖乙漓,如果已經(jīng)有線程在等待獲取鎖了级历,那么新的線程就會(huì)直接排在等待隊(duì)列后面等待獲取鎖。
- 持有鎖的線程執(zhí)行完畢釋放鎖叭披,喚醒等待隊(duì)列中的線程寥殖。
- 線程被喚醒后會(huì)嘗試獲取鎖,如果成功獲取鎖那么線程就執(zhí)行,否則線程會(huì)再次休眠等待被喚醒嚼贡。
我們?cè)谑褂肦eentrantLock的過程中熏纯,既可以構(gòu)建一個(gè)使用非公平策略的ReentrantLock實(shí)例,也可以構(gòu)建一個(gè)使用公平策略的ReentrantLock實(shí)例粤策。
ReentrantLock的類結(jié)構(gòu)
public class ReentrantLock implements Lock, java.io.Serializable {
//Sync成員變量
private final Sync sync;
//AQS的子類
abstract static class Sync extends AbstractQueuedSynchronizer {
}
//非公平策略
static final class NonfairSync extends Sync {
}
//公平策略
static final class FairSync extends Sync {
}
}
我們看到ReentrantLock類中有一個(gè)Sync類型的成員變量樟澜,Sync類繼承了AQS,然后
NonfairSync和FairSync都繼承了Sync掐场,分別實(shí)現(xiàn)非公平鎖和公平鎖往扔。
ReentrantLock的構(gòu)造函數(shù)
public ReentrantLock() {
//使用非公平策略
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
//使用公平策略
sync = fair ? new FairSync() : new NonfairSync();
}
我們可以選擇構(gòu)建公平的或非公平的ReentrantLock實(shí)例,ReentrantLock中獲取鎖和釋放鎖相關(guān)的方法如下所示熊户。我們先看非公平鎖的情況萍膛。
void lock();
boolean tryLock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
非公平的ReentrantLock
ReentrantLock的lock方法
public void lock() {
sync.lock();
}
ReentrantLock的lock方法內(nèi)調(diào)用了sync的lock方法。NonfairSync的實(shí)現(xiàn)如下所示嚷堡。
NonfairSync的lock方法
final void lock() {
//注釋1處蝗罗,
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
//注釋2處
acquire(1);
}
注釋1處,首先調(diào)用AQS的compareAndSetState方法以CAS的方式修改AQS的state
變量蝌戒,如果修改成功串塑,說明當(dāng)前線程成功獲取了鎖,然后將當(dāng)前線程設(shè)置為鎖的持有者北苟。注意是以獨(dú)占模式持有鎖的桩匪。
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
如果修改AQS的state
變量失敗,說明此時(shí)有其他線程已經(jīng)持有了鎖友鼻,那么就調(diào)用acquire(int arg)
方法獲取鎖傻昙,注意我們傳入的參數(shù)是1。
AQS的acquire(int arg)方法
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
//標(biāo)記為獨(dú)占模式
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
這個(gè)步驟可以分為3步(把大象放進(jìn)冰箱里需要幾步彩扔?)
步驟1: 調(diào)用tryAcquire(arg) 嘗試獲取鎖,獲取成功直接返回
步驟2: 嘗試獲取鎖失敗將當(dāng)前線程以獨(dú)占鎖的方式加入等待隊(duì)列
步驟3: 為已經(jīng)加入隊(duì)列中的線程嘗試獲取鎖
步驟1:調(diào)用tryAcquire(arg) 嘗試獲取鎖
AQS沒有實(shí)現(xiàn)這個(gè)方法妆档,需要子類來實(shí)現(xiàn)
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
我們看下ReentrantLock.NonfairSync類的實(shí)現(xiàn)
protected final boolean tryAcquire(int acquires) {
//調(diào)用了父類ReentrantLock.Sync的nonfairTryAcquire(acquires)方法
return nonfairTryAcquire(acquires);
}
ReentrantLock.Sync的nonfairTryAcquire(acquires)方法
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
//獲取同步狀態(tài)值
int c = getState();
//注釋1處
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
//注釋2處
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
//注釋3處
return false;
}
在注釋1處,如果同步狀態(tài)值為0虫碉,說明沒有線程持有鎖贾惦,那么就以CAS的方式修改AQS的state變量,如果修改成功敦捧,說明當(dāng)前線程成功獲取了鎖须板,然后將當(dāng)前線程設(shè)置為鎖的持有者,然后返回true兢卵,獲取鎖成功逼纸。
注釋2處,如果有線程持有鎖济蝉,并且持有鎖的線程是當(dāng)前線程杰刽,那么就將同步狀態(tài)值加1然后重新賦值給同步狀態(tài)值state菠发,然后返回true,獲取鎖成功贺嫂。
AQS的setState(int newState)方法
protected final void setState(int newState) {
state = newState;
}
注意:調(diào)用這個(gè)方法的前提是當(dāng)前線程就是鎖的持有者滓鸠,所以可以修改state值,并不需要方法同步第喳。
注釋3處糜俗,獲取鎖失敗。
到此步驟1結(jié)束曲饱,如果步驟1中獲取鎖失敗悠抹,就會(huì)進(jìn)入步驟2。
步驟2: 獲取失敗將當(dāng)前線程加入等待隊(duì)列
在這里我們要提一下AQS的一個(gè)內(nèi)部類Node扩淀。Node類是對(duì)每一個(gè)等待獲取鎖的線程的封裝楔敌,其包含了線程本身及其等待狀態(tài),如是否被阻塞驻谆、是否等待喚醒卵凑、是否已經(jīng)被取消等。還包括指向當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)的指針和后繼節(jié)點(diǎn)的指針(雙向鏈表)胜臊。Node類的成員變量waitStatus則表示當(dāng)前Node節(jié)點(diǎn)的等待狀態(tài)勺卢,共有5種取值CANCELLED、SIGNAL象对、CONDITION黑忱、PROPAGATE、0勒魔。
static final int CANCELLED = 1;
static final int SIGNAL = -1;
static final int CONDITION = -2;
static final int PROPAGATE = -3;
//默認(rèn)是0
volatile int waitStatus;
CANCELLED:表示當(dāng)前節(jié)點(diǎn)由于超時(shí)或者中斷而被取消甫煞。進(jìn)入該狀態(tài)后的節(jié)點(diǎn)狀態(tài)將不會(huì)再變化。特別的沥邻,取消節(jié)點(diǎn)的線程不會(huì)被再次阻塞危虱。
SIGNAL:當(dāng)前節(jié)點(diǎn)的后繼節(jié)點(diǎn)被阻塞了羊娃,所以當(dāng)前節(jié)點(diǎn)在釋放鎖或者取消的時(shí)候必須喚醒后繼節(jié)點(diǎn)唐全。后繼節(jié)點(diǎn)入隊(duì)時(shí),會(huì)將父節(jié)點(diǎn)的狀態(tài)更新為SIGNAL蕊玷。
CONDITION:表示節(jié)點(diǎn)正在一個(gè)條件隊(duì)列中邮利,本篇文章暫時(shí)忽略。
PROPAGATE:共享模式下垃帅,節(jié)點(diǎn)不僅會(huì)喚醒其后繼節(jié)點(diǎn)延届,同時(shí)也可能會(huì)喚醒后繼節(jié)點(diǎn)的后繼節(jié)點(diǎn)。比如當(dāng)前節(jié)點(diǎn)釋放了10個(gè)資源贸诚,當(dāng)前節(jié)點(diǎn)的后繼節(jié)點(diǎn)只需要6個(gè)節(jié)點(diǎn)方庭,那么當(dāng)前節(jié)點(diǎn)在釋放的時(shí)候就會(huì)喚醒后繼節(jié)點(diǎn)和后繼節(jié)點(diǎn)的后繼節(jié)點(diǎn)厕吉。
0:新節(jié)點(diǎn)進(jìn)入等待隊(duì)列時(shí)的默認(rèn)狀態(tài)。
注意械念,負(fù)值表示節(jié)點(diǎn)處于有效等待狀態(tài)头朱,而正值表示節(jié)點(diǎn)已被取消。所以源碼中很多地方用>0龄减、<0來判斷節(jié)點(diǎn)的狀態(tài)是否正常项钮。
private Node addWaiter(Node mode) {
//以獨(dú)占模式加入等待隊(duì)列
Node node = new Node(Thread.currentThread(), mode);
// 先嘗試最快的入隊(duì)方式
Node pred = tail;
//注釋1處
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//注釋2處
enq(node);
return node;
}
在注釋1處,如果尾節(jié)點(diǎn)不為null希停,就直接將當(dāng)前節(jié)點(diǎn)使用CAS的方式更新為尾節(jié)點(diǎn)烁巫,如果更新成功就返回node,這是最快的入隊(duì)方式宠能。
如果尾節(jié)點(diǎn)為null亚隙,或者將當(dāng)前節(jié)點(diǎn)使用CAS的方式更新為尾節(jié)點(diǎn)失敗,就調(diào)用注釋2處的enq(final Node node)
方法將node加入隊(duì)列棍潘。
AQS的enq(final Node node)方法恃鞋,注意,這個(gè)方法是一個(gè)無限循環(huán)亦歉,只有成功將加入到隊(duì)列尾部才會(huì)返回恤浪。
private Node enq(final Node node) {
for (;;) {//有一點(diǎn)疑問,這個(gè)for循環(huán)什么時(shí)候退出呢肴楷?
Node t = tail;
if (t == null) { // 如果隊(duì)列不存在水由,就新建一個(gè)node然后初始化隊(duì)列
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
首先如果尾節(jié)點(diǎn)為null,說明隊(duì)列此時(shí)還不存在赛蔫,就新建一個(gè)節(jié)點(diǎn)然后以CAS的方式將新創(chuàng)建的節(jié)點(diǎn)設(shè)置為頭節(jié)點(diǎn)砂客,如果成功則讓尾節(jié)點(diǎn)也指向node。如果如果尾節(jié)點(diǎn)不為null呵恢,就以CAS的方式將node更新為尾節(jié)點(diǎn)鞠值。
注意,這個(gè)方法是一個(gè)無限循環(huán)渗钉,只有成功將node加入到隊(duì)列尾部才會(huì)返回彤恶。
將node加入到等待隊(duì)列成功以后會(huì)進(jìn)入到AQS的acquire(int arg)方法的步驟3
步驟3: 為已經(jīng)加入隊(duì)列中的線程嘗試獲取鎖
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;//標(biāo)記是否成功獲取鎖
try {
boolean interrupted = false;//標(biāo)記線程是否被中斷
//無限循環(huán)
for (;;) {//注釋1處
//獲取前驅(qū)節(jié)點(diǎn)
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
//如果前驅(qū)節(jié)點(diǎn)是head并且嘗試獲取鎖成功,就將當(dāng)前節(jié)點(diǎn)更新為head節(jié)點(diǎn)
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
//注釋2處
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
//標(biāo)記線程在阻塞過程中是否被中斷
interrupted = true;
}
} finally {//注釋3處
if (failed)
cancelAcquire(node);
}
}
注釋1處鳄橘,如果前驅(qū)節(jié)點(diǎn)是head并且調(diào)用tryAcquire(int arg)
方法獲取鎖成功声离,就將當(dāng)前節(jié)點(diǎn)更新為head節(jié)點(diǎn),然后返回瘫怜。
private void setHead(Node node) {
head = node;
node.thread = null;
node.prev = null;
}
在setHead方法中將node的thread和prev變量都置為了null术徊,是為了幫助GC和避免不必要的喚醒和遍歷。
在注釋2處鲸湃,如果獲取鎖失敗后則判斷是否應(yīng)該阻塞當(dāng)前線程
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
//獲取前驅(qū)節(jié)點(diǎn)的狀態(tài)
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
/*
* node節(jié)點(diǎn)已經(jīng)設(shè)置了狀態(tài)告訴前驅(qū)節(jié)點(diǎn)在釋放鎖的時(shí)候通知自己赠涮,所以node節(jié)點(diǎn)可以被安全的阻塞子寓。
*/
return true;
if (ws > 0) {
/*
* 前驅(qū)節(jié)點(diǎn)已經(jīng)被取消了,向前尋找狀態(tài)有效的前驅(qū)節(jié)點(diǎn)笋除,然后將node設(shè)置為有效前驅(qū)節(jié)點(diǎn)的后繼節(jié)點(diǎn)别瞭。
* 注意:已經(jīng)被取消的節(jié)點(diǎn)會(huì)被GC,這些節(jié)點(diǎn)相當(dāng)于一個(gè)無引用鏈株憾。
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* 以CAS的方式更新前驅(qū)節(jié)點(diǎn)的waitStatus為Node.SIGNAL蝙寨,告訴前驅(qū)節(jié)點(diǎn)在釋放鎖的時(shí)候通知自己。
* 可能會(huì)失敗嗤瞎。
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
如果shouldParkAfterFailedAcquire方法返回false墙歪,那么重新循環(huán)。如果返回true則調(diào)用parkAndCheckInterrupt方法贝奇。
AQS的parkAndCheckInterrupt()方法虹菲,注意這個(gè)方法會(huì)阻塞線程,并在線程 被喚醒后掉瞳,通過調(diào)用Thread.interrupted()返回在阻塞過程中線程是否被中斷毕源。
private final boolean parkAndCheckInterrupt() {
//注釋1處
LockSupport.park(this);
//喚醒后,返回在阻塞過程中是否被中斷
return Thread.interrupted();
}
LockSupport.park(this);
注釋1處這行代碼會(huì)阻塞當(dāng)前線程陕习,Thread.interrupted()這行代碼就不會(huì)執(zhí)行了霎褐,只有被喚醒后Thread.interrupted()這行代碼才會(huì)執(zhí)行。
在線程被喚醒后该镣,返回在阻塞過程中是否被中斷冻璃。注意Thread.interrupted()
方法會(huì)將線程的中斷狀態(tài)清空。
當(dāng)線程被喚醒后损合,也會(huì)重新循環(huán)省艳。
到現(xiàn)在AQS的acquire方法就結(jié)束了。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
//中斷自己
selfInterrupt();
}
這里要注意一下嫁审,因?yàn)?code>Thread.interrupted()方法會(huì)將線程的中斷狀態(tài)清空跋炕,所以我們這里要判斷一下,如果線程在阻塞過程中被中斷了律适,我們?cè)谶@里要調(diào)用selfInterrupt()
方法來中斷當(dāng)前線程辐烂,也就是將當(dāng)前線程的中斷狀態(tài)置為true。
現(xiàn)在總結(jié)一下AQS的acquire(int arg)方法的流程擦耀。
- 調(diào)用子類的
tryAcquire(int acquires)
方法先嘗試獲取鎖棉圈,如果成功則直接返回涩堤; - 獲取失敗眷蜓,則調(diào)用
addWaiter(Node mode)
方法將該線程加入等待隊(duì)列的尾部,并標(biāo)記為獨(dú)占模式胎围; - 將該線程加入等待隊(duì)列后吁系,調(diào)用
acquireQueued(final Node node, int arg)
方法來嘗試獲取鎖德召,在這個(gè)過程中,線程可能會(huì)被多次阻塞汽纤、喚醒上岗。如果成功獲取鎖,就將當(dāng)前節(jié)點(diǎn)更新為head節(jié)點(diǎn)蕴坪,然后返回肴掷。如果在整個(gè)等待過程中被中斷過,則返回true背传,否則返回false呆瞻。 - 如果線程在等待過程中被中斷過,它是不響應(yīng)的径玖。只是獲取鎖后才再進(jìn)行自我中斷
selfInterrupt()
痴脾,將中斷補(bǔ)上。
到此梳星,非公平的ReentrantLock的lock()
方法分析完畢赞赖。
boolean tryLock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
這三個(gè)獲取鎖的方法過程也是類似的,就不進(jìn)行分析了冤灾,接下來看一看非公平的ReentrantLock釋放鎖的過程前域。
ReentrantLock的unlock()方法
public void unlock() {
//調(diào)用AQS的release方法
sync.release(1);
}
其實(shí)我們這里可以看到,一個(gè)線程可以多次獲取鎖(可重入鎖)韵吨,每獲取一次鎖就會(huì)將state加1话侄,每釋放一次鎖,就會(huì)將state減1学赛,當(dāng)前線程將state減到0的時(shí)候年堆,說明當(dāng)前線程釋放了鎖。
AQS的release(int arg)方法
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
//喚醒后繼節(jié)點(diǎn)
unparkSuccessor(h);
return true;
}
return false;
}
首先調(diào)用tryRelease(int arg)方法,AQS沒有實(shí)現(xiàn)這個(gè)方法盏浇,我們直接看ReentrantLock.Sync的實(shí)現(xiàn)
protected final boolean tryRelease(int releases) {
//同步狀態(tài)每次減1
int c = getState() - releases;
//如果當(dāng)前線程不是鎖的持有者变丧,拋出異常,沒資格釋放鎖绢掰,哈哈
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {//同步狀態(tài)為0痒蓬,表示成功釋放了鎖
free = true;
setExclusiveOwnerThread(null);
}
//更新同步狀態(tài)的值
setState(c);
return free;
}
方法首先將同步狀態(tài)值減去1,如果如果當(dāng)前線程不是鎖的持有者滴劲,拋出異常攻晒。如果同步狀態(tài)值減到了0,說明表示成功釋放了鎖班挖,然后我們將鎖的持有者設(shè)置為null鲁捏,最后更新同步狀態(tài)值,然后返回萧芙。
如果tryRelease返回了false给梅,說明沒有成功釋放鎖假丧,如果返回true,表示成功釋放了鎖动羽,那么我們要喚醒后繼節(jié)點(diǎn)包帚。
private void unparkSuccessor(Node node) {
/*
* 首先,它會(huì)檢查節(jié)點(diǎn)的waitStatus字段运吓。如果waitStatus小于0
*(表示節(jié)點(diǎn)的后繼節(jié)點(diǎn)需要喚醒)渴邦,那么它會(huì)嘗試將waitStatus設(shè)置為0。
*/
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
/*
* 然后拘哨,它會(huì)找到需要喚醒的節(jié)點(diǎn)几莽。這個(gè)節(jié)點(diǎn)通常是當(dāng)前節(jié)點(diǎn)的直接后繼節(jié)點(diǎn),
* 但是如果后繼節(jié)點(diǎn)被取消或者為null宅静,那么它會(huì)從隊(duì)列的尾部開始向前遍歷章蚣,
* 找到第一個(gè)未被取消的節(jié)點(diǎn)。
*/
Node s = node.next;
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)纤垂,那么它會(huì)調(diào)用LockSupport.unpark方法來喚醒節(jié)點(diǎn)對(duì)應(yīng)的線程。
if (s != null)
LockSupport.unpark(s.thread);
}
被喚醒的線程會(huì)從上面的parkAndCheckInterrupt方法中第二行代碼恢復(fù)執(zhí)行
private final boolean parkAndCheckInterrupt() {
//注釋1處
LockSupport.park(this);
//喚醒后磷账,在這里恢復(fù)執(zhí)行峭沦,返回在阻塞過程中是否被中斷
return Thread.interrupted();
}
總結(jié)一下AQS的release(int arg)方法的流程。
release方法每次釋放鎖就會(huì)將state值減1逃糟,如果徹底釋放了(即state==0)吼鱼,就會(huì)喚醒等待隊(duì)列里的其他線程來獲取鎖。
看完了非公平的ReentrantLock獲取鎖和釋放鎖的過程绰咽,接下來我們看看公平的ReentrantLock獲取鎖和釋放鎖過程菇肃。
公平的ReentrantLock
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
當(dāng)我們使用上面的構(gòu)造函數(shù)創(chuàng)建ReentrantLock實(shí)例的時(shí)候,如果傳入的參數(shù)是true取募,那么構(gòu)建的是公平的ReentrantLock
public void lock() {
sync.lock();
}
ReentrantLock.FairSync的lock方法
final void lock() {
//獲取鎖
acquire(1);
}
AQS的acquire(int arg)方法
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
這個(gè)步驟和非公平策略獲取鎖是一樣的可以分為3步
步驟1: 調(diào)用tryAcquire(arg) 嘗試獲取鎖琐谤,獲取成功直接返回
步驟2: 獲取失敗將當(dāng)前線程以獨(dú)占鎖的方式加入等待隊(duì)列
步驟3: 為已經(jīng)加入隊(duì)列中的線程嘗試獲取鎖
步驟1: 調(diào)用tryAcquire(arg) 嘗試獲取鎖
ReentrantLock.FairSync的tryAcquire(int acquires)方法
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//注釋1處
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//注釋2處
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
在上面方法的注釋1處,c==0表示沒有線程持有鎖玩敏,首先調(diào)用hasQueuedPredecessors
方法判斷等待隊(duì)列里面是否有節(jié)點(diǎn)斗忌,如果有等待節(jié)點(diǎn),返回true旺聚,那么當(dāng)前線程就不去獲取鎖(體現(xiàn)了公平)织阳。如果沒有等待節(jié)點(diǎn)并且以CAS的方式獲取鎖成功則將當(dāng)前線程賦值為持有鎖的線程,返回true砰粹。
在注釋2處唧躲,c!=0表示有線程持有鎖,如果是當(dāng)前線程持有鎖的話,那么就將同步狀態(tài)值加1惊窖,返回true。
步驟2和步驟3和非公平的ReentrantLock是一樣的厘贼,就不再贅述了界酒。
公平的ReentrantLock和非公平的ReentrantLock的release(int arg)
方法也是一樣的就不再贅述了。
公平的ReentrantLock和非公平的ReentrantLock的差異
- 公平的ReentrantLock和非公平的ReentrantLock的差異由
ReentrantLock.FairSync
和ReentrantLock.NonfairSync
體現(xiàn)嘴秸。非公平鎖總會(huì)先嘗試獲取鎖毁欣。如下圖所示:左邊是公平鎖,右邊是非公平鎖岳掐。
lock方法
tryAcquire 方法
- 如果等待隊(duì)列里有節(jié)點(diǎn)等待獲取鎖凭疮,公平的ReentrantLock就會(huì)直接進(jìn)入等待隊(duì)列排隊(duì)。非公平的ReentrantLock無論等待隊(duì)列里是否有節(jié)點(diǎn)等待獲取鎖串述,總是先嘗試獲取鎖执解,如果獲取失敗才進(jìn)入等待隊(duì)列進(jìn)行排隊(duì)。
結(jié)尾:本篇文章通過ReentrantLock引出了AQS是如何幫助ReentrantLock實(shí)現(xiàn)的獲取鎖和釋放鎖的纲酗。下一篇文章打算分析一下AQS是如何幫助ReentrantLock實(shí)現(xiàn)Condition
功能的衰腌。
參考鏈接: