在看這篇文章之前可以根據(jù)我上一篇文章來對隊(duì)列同步器AQS的應(yīng)用和意義有一個(gè)基礎(chǔ)印象文捶。
http://www.reibang.com/p/e2f339d654f7
從實(shí)現(xiàn)角度分析同步器是如何完成線程同步的壁熄,主要包括:同步隊(duì)列、獨(dú)占式同步狀態(tài)獲取與釋放空免、共享式同步狀態(tài)獲取與釋放以及超時(shí)獲取同步狀態(tài)等同步器的核心數(shù)據(jù)結(jié)構(gòu)與模板方法。
1.同步隊(duì)列
同步器依賴內(nèi)部的同步隊(duì)列(一個(gè)FIFO雙向隊(duì)列)來完成同步狀態(tài)的管理盆耽,當(dāng)前線程獲取同步狀態(tài)失敗時(shí)蹋砚,同步器會將當(dāng)前線程以及等待狀態(tài)等信息構(gòu)造成為一個(gè)節(jié)點(diǎn)(Node)并將其加入同步隊(duì)列,同時(shí)會阻塞當(dāng)前線程摄杂,當(dāng)同步狀態(tài)釋放時(shí)坝咐,會把首節(jié)點(diǎn)中的線程喚醒,使其再次嘗試獲取同步狀態(tài)析恢。
同步隊(duì)列中的節(jié)點(diǎn)(Node)用來保存獲取同步狀態(tài)失敗的線程引用墨坚、等待狀態(tài)以及前驅(qū)和后繼節(jié)點(diǎn)鹃愤,節(jié)點(diǎn)的屬性類型與名稱以及描述如表:同步器包含了兩個(gè)節(jié)點(diǎn)類型的引用袖肥,一個(gè)指向頭節(jié)點(diǎn)咪辱,而另一個(gè)指向尾節(jié)點(diǎn)。試想一下椎组,當(dāng)一個(gè)線程成功地獲取了同步狀態(tài)(或者鎖)油狂,其他線程將無法獲取到同步狀態(tài),轉(zhuǎn)而被構(gòu)造成為節(jié)點(diǎn)并加入到同步隊(duì)列中寸癌,而這個(gè)加入隊(duì)列的過程必須要保證線程安全蒸苇,因此同步器提供了一個(gè)基于CAS的設(shè)置尾節(jié)點(diǎn)的方法:compareAndSetTail(Node expect,Node update)庇勃,它需要傳遞當(dāng)前線程“認(rèn)為”的尾節(jié)點(diǎn)和當(dāng)前節(jié)點(diǎn)罕拂,只有設(shè)置成功后柿菩,當(dāng)前節(jié)點(diǎn)才正式與之前的尾節(jié)點(diǎn)建立關(guān)聯(lián)镜悉。
static final class Node {
static final Node SHARED = new Node();
static final Node EXCLUSIVE = null;
static final int CANCELLED = 1//節(jié)點(diǎn)從同步隊(duì)列中取消
static final int SIGNAL = -1//后繼節(jié)點(diǎn)的線程處于等待狀態(tài)稼锅,如果當(dāng)前節(jié)點(diǎn)釋放同步狀態(tài)會通知后繼節(jié)點(diǎn)锥债,使得后繼節(jié)點(diǎn)的線程能夠運(yùn)行允趟;
static final int CONDITION = -2//當(dāng)前節(jié)點(diǎn)進(jìn)入等待隊(duì)列中
static final int PROPAGATE = -3//表示下一次共享式同步狀態(tài)獲取將會無條件傳播下去
static final int INITIAL = 0;//初始狀態(tài)
volatile int waitStatus //節(jié)點(diǎn)狀態(tài)
volatile Node prev //當(dāng)前節(jié)點(diǎn)/線程的前驅(qū)節(jié)點(diǎn)
volatile Node next; //當(dāng)前節(jié)點(diǎn)/線程的后繼節(jié)點(diǎn)
volatile Thread thread;//加入同步隊(duì)列的線程引用
Node nextWaiter;//等待隊(duì)列中的下一個(gè)節(jié)點(diǎn)
final boolean isShared() {
return nextWaiter == SHARED;
}
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
Node() {
}
Node(Thread thread, Node mode) {
this.nextWaiter = mode;
this.thread = thread;
}
Node(Thread thread, int waitStatus) { // Used by Condition
this.waitStatus = waitStatus;
this.thread = thread;
}
}
同步器將節(jié)點(diǎn)加入到同步隊(duì)列的過程如圖:
節(jié)點(diǎn)加入到同步隊(duì)列同步隊(duì)列遵循FIFO,首節(jié)點(diǎn)是獲取同步狀態(tài)成功的節(jié)點(diǎn),首節(jié)點(diǎn)的線程在釋放同步狀態(tài)時(shí),將會喚醒后繼節(jié)點(diǎn),而后繼節(jié)點(diǎn)將會在獲取同步狀態(tài)成功時(shí)將自己設(shè)置為首節(jié)點(diǎn),該過程如圖:
設(shè)置首節(jié)點(diǎn)是通過獲取同步狀態(tài)成功的線程來完成的斑鼻,由于只有一個(gè)線程能夠成功獲取到同步狀態(tài)荒叶,因此設(shè)置頭節(jié)點(diǎn)的方法并不需要使用CAS來保證,它只需要將首節(jié)點(diǎn)設(shè)置成為原首節(jié)點(diǎn)的后繼節(jié)點(diǎn)并斷開原首節(jié)點(diǎn)的next引用即可。
2.獨(dú)占式同步狀態(tài)獲取與釋放
通過調(diào)用同步器的acquire(int arg)方法可以獲取同步狀態(tài),該方法對中斷不敏感胸蛛,也就是由于線程獲取同步狀態(tài)失敗后進(jìn)入同步隊(duì)列中民珍,后續(xù)對線程進(jìn)行中斷操作時(shí)逆趣,線程不會從同步隊(duì)列中移出痕囱,該方法代碼:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
//tryAcquire(arg) 嘗試以獨(dú)占模式獲取巷查。 該方法應(yīng)該查詢對象的狀態(tài)是否允許以獨(dú)占模式獲取崇败,如果是,則獲取它。
//該方法總是由執(zhí)行獲取的線程調(diào)用予弧。 如果此方法報(bào)告失敗蚓庭,則獲取方法可能將線程排隊(duì)(如果尚未排隊(duì))拳魁,
//直到被其他線程釋放為止贿衍。
/**
* addWaiter(Node.EXCLUSIVE) 為當(dāng)前線程和給定模式創(chuàng)建和排隊(duì)節(jié)點(diǎn)。
* selfInterrupt Thread.currentThread().interrupt();
*/
主要完成了同步狀態(tài)獲取、節(jié)點(diǎn)構(gòu)造、加入同步隊(duì)列以及在同步隊(duì)列中自旋等待的相關(guān)工作,其主要邏輯是:首先調(diào)用自定義同步器實(shí)現(xiàn)的tryAcquire(int arg)方法,該方法保證線程安全的獲取同步狀態(tài),如果同步狀態(tài)獲取失敗,則構(gòu)造同步節(jié)點(diǎn)(獨(dú)占式Node.EXCLUSIVE,同一時(shí)刻只能有一個(gè)線程成功獲取同步狀態(tài))并通過addWaiter(Node node)该窗。
方法將該節(jié)點(diǎn)加入到同步隊(duì)列的尾部,最后調(diào)用acquireQueued(Node node,int arg)方法蚤霞,使得該節(jié)點(diǎn)以“死循環(huán)”的方式獲取同步狀態(tài)酗失。如果獲取不到則阻塞節(jié)點(diǎn)中的線程,而被阻塞線程的喚醒主要依靠前驅(qū)節(jié)點(diǎn)的出隊(duì)或阻塞線程被中斷來實(shí)現(xiàn)昧绣。
分析一下相關(guān)工作。首先是節(jié)點(diǎn)的構(gòu)造以及加入同步隊(duì)列:
private Node addWaiter(Node mode) {
// 1. 將當(dāng)前線程構(gòu)建成Node類型
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
// 2. 當(dāng)前尾節(jié)點(diǎn)是否為null?
Node pred = tail;
if (pred != null) {
// 2.2 將當(dāng)前節(jié)點(diǎn)尾插入的方式插入同步隊(duì)列中
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
// 2.1. 當(dāng)前同步隊(duì)列尾節(jié)點(diǎn)為null贪绘,說明當(dāng)前線程是第一個(gè)加入同步隊(duì)列進(jìn)行等待的線程
enq(node);
return node;
}
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
//1. 構(gòu)造頭結(jié)點(diǎn)
if (compareAndSetHead(new Node()))
tail = head;
} else {
// 2. 尾插入,CAS操作失敗自旋嘗試
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
通過使用compareAndSetTail(Node expect,Node update)方法來確保節(jié)點(diǎn)能夠被線程安全添加.compareAndSetTail該方法由unsafe類提供狸窘,有關(guān)于想要深入了解UnSafe類得墩朦,可以看以下得文章:
https://www.cnblogs.com/throwable/p/9139947.html
如果使用一個(gè)普通的LinkedList來維護(hù)節(jié)點(diǎn)之間的關(guān)系,那么當(dāng)一個(gè)線程獲取了同步狀態(tài)牛哺,而其他多個(gè)線程由于調(diào)用tryAcquire(int arg)方法獲取同步狀態(tài)失敗而并發(fā)地被添加到LinkedList時(shí),LinkedList將難以保證Node的正確添加引润,最終的結(jié)果可能是節(jié)點(diǎn)的數(shù)量有偏差议慰,而且順序也是混亂的蠢古。
在enq(final Node node)方法中,同步器通過“死循環(huán)”來保證節(jié)點(diǎn)的正確添加别凹,在“死循環(huán)”中只有通過CAS將節(jié)點(diǎn)設(shè)置成為尾節(jié)點(diǎn)之后草讶,當(dāng)前線程才能從該方法返回,否則炉菲,當(dāng)前線程不斷地嘗試設(shè)置堕战。可以看出拍霜,enq(final Node node)方法將并發(fā)添加節(jié)點(diǎn)的請求通過CAS變得“串行化”了嘱丢。
節(jié)點(diǎn)進(jìn)入同步隊(duì)列之后,就進(jìn)入了一個(gè)自旋的過程祠饺,每個(gè)節(jié)點(diǎn)(或者說每個(gè)線程)都在自省地觀察越驻,當(dāng)條件滿足,獲取到了同步狀態(tài)道偷,就可以從這個(gè)自旋過程中退出伐谈,否則依舊留在這個(gè)自旋過程中(并會阻塞節(jié)點(diǎn)的線程)
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
// 1. 獲得當(dāng)前節(jié)點(diǎn)的先驅(qū)節(jié)點(diǎn)
final Node p = node.predecessor();
// 2. 當(dāng)前節(jié)點(diǎn)能否獲取獨(dú)占式鎖
// 2.1 如果當(dāng)前節(jié)點(diǎn)的先驅(qū)節(jié)點(diǎn)是頭結(jié)點(diǎn)并且成功獲取同步狀態(tài),即可以獲得獨(dú)占式鎖
if (p == head && tryAcquire(arg)) {
//隊(duì)列頭指針用指向當(dāng)前節(jié)點(diǎn)
setHead(node);
//釋放前驅(qū)節(jié)點(diǎn)
p.next = null; // help GC
failed = false;
return interrupted;
}
// 2.2 獲取鎖失敗试疙,線程進(jìn)入等待狀態(tài)等待獲取獨(dú)占式鎖
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
return true;
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
shouldParkAfterFailedAcquire()方法主要邏輯是使用compareAndSetWaitStatus(pred, ws, Node.SIGNAL)使用CAS將節(jié)點(diǎn)狀態(tài)由INITIAL設(shè)置成SIGNAL诵棵,表示當(dāng)前線程阻塞。當(dāng)compareAndSetWaitStatus設(shè)置失敗則說明shouldParkAfterFailedAcquire方法返回false祝旷,然后會在acquireQueued()方法中for (;;)死循環(huán)中會繼續(xù)重試履澳,直至compareAndSetWaitStatus設(shè)置節(jié)點(diǎn)狀態(tài)位為SIGNAL時(shí)shouldParkAfterFailedAcquire返回true時(shí)才會執(zhí)行方法parkAndCheckInterrupt()方法,該方法的源碼為:
private final boolean parkAndCheckInterrupt() {
//使得該線程阻塞
LockSupport.park(this);
return Thread.interrupted();
}
acquireQueued()在自旋過程中主要完成了兩件事情:
如果當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)是頭節(jié)點(diǎn)怀跛,并且能夠獲得同步狀態(tài)的話距贷,當(dāng)前線程能夠獲得鎖該方法執(zhí)行結(jié)束退出;
獲取鎖失敗的話吻谋,先將節(jié)點(diǎn)狀態(tài)設(shè)置成SIGNAL忠蝗,然后調(diào)用LookSupport.park方法使得當(dāng)前線程阻塞。
在acquireQueued(final Node node,int arg)方法中漓拾,當(dāng)前線程在“死循環(huán)”中嘗試獲取同步狀態(tài)阁最,而只有前驅(qū)節(jié)點(diǎn)是頭節(jié)點(diǎn)才能夠嘗試獲取同步狀態(tài),這是為什么骇两?原因有兩個(gè)速种,如下。
第一低千,頭節(jié)點(diǎn)是成功獲取到同步狀態(tài)的節(jié)點(diǎn)配阵,而頭節(jié)點(diǎn)的線程釋放了同步狀態(tài)之后,將會喚醒其后繼節(jié)點(diǎn),后繼節(jié)點(diǎn)的線程被喚醒后需要檢查自己的前驅(qū)節(jié)點(diǎn)是否是頭節(jié)點(diǎn)棋傍。
第二救拉,維護(hù)同步隊(duì)列的FIFO原則。該方法中瘫拣,節(jié)點(diǎn)自旋獲取同步狀態(tài)的行為如圖:
前驅(qū)節(jié)點(diǎn)為頭節(jié)點(diǎn)且能夠獲取同步狀態(tài)的判斷條件和線程進(jìn)入等待狀態(tài)是獲
取同步狀態(tài)的自旋過程。當(dāng)同步狀態(tài)獲取成功之后拂铡,當(dāng)前線程從acquire(int arg)方法返回壹无,如果對于鎖這種并發(fā)組件而言,代表著當(dāng)前線程獲取了鎖感帅。
當(dāng)前線程獲取同步狀態(tài)并執(zhí)行了相應(yīng)邏輯之后斗锭,就需要釋放同步狀態(tài),使得后續(xù)節(jié)點(diǎn)能夠繼續(xù)獲取同步狀態(tài)失球。通過調(diào)用同步器的release(int arg)方法可以釋放同步狀態(tài)岖是,該方法在釋放了同步狀態(tài)之后,會喚醒其后繼節(jié)點(diǎn)(進(jìn)而使后繼節(jié)點(diǎn)重新嘗試獲取同步狀態(tài))实苞。該方法代碼如:
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
該方法執(zhí)行時(shí)豺撑,會喚醒頭節(jié)點(diǎn)的后繼節(jié)點(diǎn)線程,unparkSuccessor(Node node)方法使用LockSupport來喚醒處于等待狀態(tài)的線程黔牵。
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
//頭節(jié)點(diǎn)的后繼節(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;
}
if (s != null)
//后繼節(jié)點(diǎn)不為null時(shí)喚醒該線程
LockSupport.unpark(s.thread);
}
源碼的關(guān)鍵信息請看注釋聪轿,首先獲取頭節(jié)點(diǎn)的后繼節(jié)點(diǎn),當(dāng)后繼節(jié)點(diǎn)為null的時(shí)候會調(diào)用LookSupport.unpark()方法猾浦,該方法會喚醒該節(jié)點(diǎn)的后繼節(jié)點(diǎn)所包裝的線程陆错。因此,每一次鎖釋放后就會喚醒隊(duì)列中該節(jié)點(diǎn)的后繼節(jié)點(diǎn)所引用的線程金赦,從而進(jìn)一步可以佐證獲得鎖的過程是一個(gè)FIFO(先進(jìn)先出)的過程音瓷。
線程獲取鎖失敗,線程被封裝成Node進(jìn)行入隊(duì)操作夹抗,核心方法在于addWaiter()和enq()绳慎,同時(shí)enq()完成對同步隊(duì)列的頭結(jié)點(diǎn)初始化工作以及CAS操作失敗的重試;
線程獲取鎖是一個(gè)自旋的過程,當(dāng)且僅當(dāng) 當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)是頭結(jié)點(diǎn)并且成功獲得同步狀態(tài)時(shí)漠烧,節(jié)點(diǎn)出隊(duì)即該節(jié)點(diǎn)引用的線程獲得鎖杏愤,否則,當(dāng)不滿足條件時(shí)就會調(diào)用LookSupport.park()方法使得線程阻塞沽甥;
釋放鎖的時(shí)候會喚醒后繼節(jié)點(diǎn)声邦;
總結(jié):在獲取同步狀態(tài)時(shí),同步器維護(hù)一個(gè)同步隊(duì)列摆舟,獲取狀態(tài)失敗的線程都會被加入到隊(duì)列中并在隊(duì)列中進(jìn)行自旋;移出隊(duì)列(或停止自旋)的條件是前驅(qū)節(jié)點(diǎn)為頭節(jié)點(diǎn)且成功獲取了同步狀態(tài)。在釋放同步狀態(tài)時(shí)恨诱,同步器調(diào)用tryRelease(int arg)方法釋放同步狀態(tài)媳瞪,然后喚醒頭節(jié)點(diǎn)的后繼節(jié)點(diǎn)。
可中斷式獲取鎖(acquireInterruptibly方法)
可響應(yīng)中斷式鎖可調(diào)用方法lock.lockInterruptibly();而該方法其底層會調(diào)用AQS的acquireInterruptibly方法照宝,源碼為:
public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
//線程獲取鎖失敗
doAcquireInterruptibly(arg);
}
在獲取同步狀態(tài)失敗后就會調(diào)用doAcquireInterruptibly方法:
private void doAcquireInterruptibly(int arg)
throws InterruptedException {
//將節(jié)點(diǎn)插入到同步隊(duì)列中
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
//獲取鎖出隊(duì)
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);
}
}
與acquire方法邏輯幾乎一致蛇受,唯一的區(qū)別是當(dāng)parkAndCheckInterrupt返回true時(shí)即線程阻塞時(shí)該線程被中斷,代碼拋出被中斷異常厕鹃。
超時(shí)等待式獲取同步狀態(tài)(tryAcquireNanos()方法)
通過調(diào)用lock.tryLock(timeout,TimeUnit)方式達(dá)到超時(shí)等待獲取鎖的效果兢仰,該方法會在三種情況下才會返回:
在超時(shí)時(shí)間內(nèi),當(dāng)前線程成功獲取了鎖剂碴;
當(dāng)前線程在超時(shí)時(shí)間內(nèi)被中斷把将;
超時(shí)時(shí)間結(jié)束,仍未獲得鎖返回false忆矛。
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquire(arg) ||
//實(shí)現(xiàn)超時(shí)等待的效果
doAcquireNanos(arg, nanosTimeout);
}
通過調(diào)用同步器的doAcquireNanos(int arg,long nanosTimeout)方法可以超時(shí)獲取同步狀態(tài)察蹲,即在指定的時(shí)間段內(nèi)獲取同步狀態(tài),如果獲取到同步狀態(tài)則返回true催训,否則洽议,返回false。該方法提供了傳統(tǒng)Java同步操作(比如synchronized關(guān)鍵字)所不具備的特性漫拭。
在分析該方法的實(shí)現(xiàn)前亚兄,先介紹一下響應(yīng)中斷的同步狀態(tài)獲取過程。在Java 5之前采驻,當(dāng)一個(gè)線程獲取不到鎖而被阻塞在synchronized之外時(shí)儿捧,對該線程進(jìn)行中斷操作,此時(shí)該線程的中斷標(biāo)志位會被修改挑宠,但線程依舊會阻塞在synchronized上菲盾,等待著獲取鎖。在Java 5中各淀,同步器提供了acquireInterruptibly(int arg)方法懒鉴,這個(gè)方法在等待獲取同步狀態(tài)時(shí),如果當(dāng)前線程被中斷碎浇,會立刻返回并拋出InterruptedException临谱。
超時(shí)獲取同步狀態(tài)過程可以被視作響應(yīng)中斷獲取同步狀態(tài)過程的“增強(qiáng)版”,
doAcquireNanos(int arg,long nanosTimeout)方法在支持響應(yīng)中斷的基礎(chǔ)上奴璃,增加超時(shí)獲取的特性悉默。針對超時(shí)獲取,主要需要計(jì)算出需要睡眠的時(shí)間間隔nanosTimeout苟穆,為了防止過早通知抄课,nanosTimeout計(jì)算公式為:nanosTimeout-=now-lastTime唱星,其中now為當(dāng)前喚醒時(shí)間,lastTime為上次喚醒時(shí)間跟磨,如果nanosTimeout大于0則表示超時(shí)時(shí)間未到间聊,需要繼續(xù)睡眠nanosTimeout納秒,反之抵拘,表示已經(jīng)超超時(shí)哎榴。
代碼:
private boolean doAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (nanosTimeout <= 0L)
return false;
//1. 根據(jù)超時(shí)時(shí)間和當(dāng)前時(shí)間計(jì)算出截止時(shí)間
final long deadline = System.nanoTime() + nanosTimeout;
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
//如果獲取到鎖,則把頭節(jié)點(diǎn)出隊(duì)列僵蛛,并返回尚蝌。
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return true;
}
//獲取不到同步狀態(tài),則計(jì)算當(dāng)前時(shí)間充尉,判斷是否超時(shí)飘言,超時(shí)返回false
nanosTimeout = deadline - System.nanoTime();
if (nanosTimeout <= 0L)
return false;
// 3.線程阻塞等待
if (shouldParkAfterFailedAcquire(p, node) &&
nanosTimeout > spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanosTimeout);
// 如果被中斷,拋異常喉酌。
if (Thread.interrupted())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
該方法在自旋過程中热凹,當(dāng)節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)為頭節(jié)點(diǎn)時(shí)嘗試獲取同步狀態(tài),如果獲取成功則從該方法返回泪电,這個(gè)過程和獨(dú)占式同步獲取的過程類似般妙,但是在同步狀態(tài)獲取失敗的處理上有所不同。如果當(dāng)前線程獲取同步狀態(tài)失敗相速,則判斷是否超時(shí)(nanosTimeout小于等于0表示已經(jīng)超時(shí))碟渺,如果沒有超時(shí),重新計(jì)算超時(shí)間隔nanosTimeout突诬,然后使當(dāng)前線程等待nanosTimeout納秒(當(dāng)已到設(shè)置的超時(shí)時(shí)間苫拍,該線程會從LockSupport.parkNanos(Object blocker,long nanos)方法返回)。這個(gè)后續(xù)看LockSupport的源碼旺隙。
如果nanosTimeout小于等于spinForTimeoutThreshold(1000納秒)時(shí)绒极,將不會使該線程進(jìn)行超時(shí)等待,而是進(jìn)入快速的自旋過程蔬捷。原因在于垄提,非常短的超時(shí)等待無法做到十分精確,如果這時(shí)再進(jìn)行超時(shí)等待周拐,相反會讓nanosTimeout的超時(shí)從整體上表現(xiàn)得反而不精確铡俐。因此,在超時(shí)非常短的場景下妥粟,同步器會進(jìn)入無條件的快速自旋审丘。
待續(xù)二,休息一下