摘要:無(wú)論是公平鎖還是非公平鎖,它們的實(shí)現(xiàn)都依賴(lài)于,它提供了一個(gè)基于先進(jìn)先出等待隊(duì)列實(shí)現(xiàn)和的框架。特性如下僅通過(guò)一個(gè)類(lèi)型來(lái)代表狀態(tài)。等喚醒的時(shí)候,重新獲取鎖,并清掉中的線(xiàn)程。
無(wú)論是公平鎖還是非公平鎖,它們的實(shí)現(xiàn)都依賴(lài)于AbstractQueuedSynchronizer,它提供了一個(gè)基于先進(jìn)先出等待隊(duì)列 實(shí)現(xiàn)block locks和synchronizers的框架。特性如下
僅通過(guò)一個(gè) int 類(lèi)型來(lái)代表狀態(tài)。對(duì)于ReentrantLock而言,他就是線(xiàn)程持有鎖的次數(shù),當(dāng)次數(shù)為0時(shí),代表鎖沒(méi)有被持有,正數(shù)代表被持有的次數(shù),負(fù)數(shù)則是超出了鎖的持有范圍,有可能存在死循環(huán)
支持獨(dú)占模式(默認(rèn)的)和共享模式。在獨(dú)占模式中,去獲取一個(gè)已經(jīng)被其它線(xiàn)程擁有的鎖只會(huì)失敗,共享模式中,則多個(gè)線(xiàn)程是可以成功的。
lock()原理當(dāng)ReentrantLock獲取鎖失敗時(shí),會(huì)執(zhí)行 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
private Node addWaiter(Node mode) { Node node = new Node(Thread.currentThread(), mode); //創(chuàng)建一個(gè)節(jié)點(diǎn),存儲(chǔ)當(dāng)前的線(xiàn)程,以及鎖持有的模式,對(duì)于 ReentrantLock來(lái)說(shuō)就是 獨(dú)占 型 // Try the fast path of enq; backup to full enq on failure Node pred = tail; if (pred != null) { node.prev = pred; if (compareAndSetTail(pred, node)) {//CAS操作,如果當(dāng)前的尾部節(jié)點(diǎn)沒(méi)有被其它線(xiàn)程更改,那么把新的節(jié)點(diǎn)設(shè)置成隊(duì)列的尾部 pred.next = node; return node; } } enq(node);//首次入隊(duì) return node; }
獲取失敗進(jìn)行入隊(duì)操作,首先就是往隊(duì)列中添加一個(gè)正在等待的節(jié)點(diǎn)Node
從Node本身的結(jié)構(gòu)可以看到,AQS(AbstractQueuedSynchronizer)本身就維護(hù)了一個(gè)雙向鏈表,用來(lái)存放等待中的線(xiàn)程。鏈表的每個(gè)節(jié)點(diǎn),代表那個(gè)線(xiàn)程,是獨(dú)占還是共享鎖。
創(chuàng)建好節(jié)點(diǎn)之后,便執(zhí)行入隊(duì)操作,對(duì)于首次創(chuàng)建隊(duì)列
private Node enq(final Node node) { for (;;) { //借助CAS機(jī)制實(shí)現(xiàn)無(wú)鎖操作,所以需要一直執(zhí)行直到CAS成功 Node t = tail; if (t == null) { // 初始化發(fā)生在第一次創(chuàng)建隊(duì)列,這樣的好處是,當(dāng)競(jìng)爭(zhēng)不激烈的時(shí)候,實(shí)際上也就不會(huì)發(fā)生這些操作,性能也會(huì)好些 if (compareAndSetHead(new Node())) tail = head; } else { node.prev = t; if (compareAndSetTail(t, node)) { t.next = node; return t; } } } }
可以看到,入隊(duì)也就是從隊(duì)尾插入新的等待線(xiàn)程,入隊(duì)完畢,也就開(kāi)始去進(jìn)行不斷的嘗試,直到獲取鎖成功,可以看到,對(duì)于lock來(lái)說(shuō),其實(shí)已經(jīng)是阻塞了
final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; for (;;) { final Node p = node.predecessor(); //優(yōu)先執(zhí)行當(dāng)前節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn) if (p == head && tryAcquire(arg)) { //僅當(dāng)當(dāng)前節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)是head,才去獲取線(xiàn)程,這里可以看出其實(shí)先等待的線(xiàn)程是會(huì)優(yōu)先處理,也就是FIFO原則 setHead(node); p.next = null; // help GC ,釋放掉當(dāng)前線(xiàn)程在隊(duì)列中的引用,也可以看做’出隊(duì)"了 failed = false; //執(zhí)行到這里說(shuō)明獲取鎖成功 return interrupted; } //執(zhí)行到這里說(shuō)明存在競(jìng)爭(zhēng),有多個(gè)線(xiàn)程都在等待一個(gè)鎖 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) //里面會(huì)對(duì)當(dāng)前線(xiàn)程執(zhí)行中斷,當(dāng)被喚醒時(shí),繼續(xù)循環(huán) //如果線(xiàn)程被中斷,設(shè)置中斷標(biāo)記,區(qū)別于 doAcquireInterruptibly,doAcquireInterruptibly是直接拋出異常,這也就是 lockInterruptibly能夠拋出中斷的原因 interrupted = true; } } finally { if (failed) cancelAcquire(node); } }
從這里可以看到,無(wú)論鎖是公平鎖還是非公平鎖,只要被放入了等待隊(duì)列,此時(shí)的執(zhí)行依然是誰(shuí)先等待就先執(zhí)行誰(shuí) ,非公平鎖體現(xiàn)在新來(lái)的線(xiàn)程會(huì)無(wú)視已經(jīng)等了的線(xiàn)程,可以?xún)?yōu)先去搶鎖,所以公平體現(xiàn)在第一次參與搶鎖的線(xiàn)程會(huì)去等待已經(jīng)在等待隊(duì)列中的線(xiàn)程,非公平并不是說(shuō)從已經(jīng)在等待的線(xiàn)程隊(duì)列里面隨便選一個(gè)
shouldParkAfterFailedAcquire的源碼如下
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { int ws = pred.waitStatus; //查看前一個(gè)節(jié)點(diǎn)的等待狀態(tài) if (ws == Node.SIGNAL) //已經(jīng)嘗試過(guò)獲取鎖,可以執(zhí)行park了 return true; if (ws > 0) { do { //去掉隊(duì)列中所有已經(jīng)取消的線(xiàn)程 node.prev = pred = pred.prev; } while (pred.waitStatus > 0); pred.next = node; } else { //此時(shí)當(dāng)前線(xiàn)程的前一個(gè)節(jié)點(diǎn)的等待狀態(tài)必定是0或者PROGATE,這表明當(dāng)前線(xiàn)程在park之前可以再?lài)L試一次去獲取鎖,也就是說(shuō)前一個(gè)節(jié)點(diǎn)可能剛獲取到SIGNAL compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; }
waitStatus:等待的狀態(tài),共有5種
SIGNAL:,表明它的前一個(gè)節(jié)點(diǎn)需要執(zhí)行 unparking;
CANCELLED:當(dāng)前節(jié)點(diǎn)保存的線(xiàn)程由于超時(shí)或者中斷被取消了;
CONDITION:接檔正處于條件隊(duì)列中,執(zhí)行了await;
PROPAGATE:一個(gè)共享的鎖需要傳遞釋放信號(hào)到其它節(jié)點(diǎn)
0:非上述4中狀態(tài),有可能是剛獲取signal,此時(shí)它的值是0,也有可能是新建的head節(jié)點(diǎn)
parkAndCheckInterrupt主要是park當(dāng)前線(xiàn)程
private final boolean parkAndCheckInterrupt() { //當(dāng)獲取不到許可時(shí),阻塞線(xiàn)程,解除阻塞狀態(tài)的情況如下: //1 某個(gè)線(xiàn)程對(duì)這個(gè)線(xiàn)程調(diào)用了unpark方法 //2 某個(gè)線(xiàn)程中斷了這個(gè)線(xiàn)程 //3 這個(gè)方法毫無(wú)理由的返回了 [park比較奇特的地方],基于這樣,調(diào)用的時(shí)候必須去判斷park的條件,以及當(dāng)它返回的時(shí)候,去設(shè)置中斷的狀態(tài) LockSupport.park(this); //返回線(xiàn)程的中斷狀態(tài) return Thread.interrupted(); }
至此lock()執(zhí)行結(jié)束
unlock()原理當(dāng)執(zhí)行unlock時(shí),ReentrentLock執(zhí)行對(duì)應(yīng)的Release
public final boolean release(int arg) { if (tryRelease(arg)) { //執(zhí)行這里表示所已經(jīng)被釋放,可以讓它的下一個(gè)節(jié)點(diǎn)來(lái)?yè)屾i Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); //h.waitStatus == 0 表示還沒(méi)有執(zhí)行park,自然不需要unpark return true; } return false; }
如果release成功,即當(dāng)前線(xiàn)程持有的所有鎖都已經(jīng)釋放,那么就可以執(zhí)行 unparkSuccessor,從源碼可以看到,unpark是從頭部開(kāi)始進(jìn)行的,結(jié)合lock的原理,可知AQS本身就是一個(gè)先進(jìn)先出的隊(duì)列
unparkSuccessor源碼如下
private void unparkSuccessor(Node node) { int ws = node.waitStatus; if (ws < 0) compareAndSetWaitStatus(node, ws, 0); Node s = node.next; //有可能線(xiàn)程被取消了,所以從尾部往前遍歷,到最近一個(gè)沒(méi)有被取消的線(xià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; //確保線(xiàn)程沒(méi)有取消 } if (s != null) LockSupport.unpark(s.thread); //恢復(fù)線(xiàn)程 }
至此unlock()完畢
await的原理public final void await() throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException();//當(dāng)前線(xiàn)程已經(jīng)中斷了,拋出中斷異常 //添加一個(gè)新的waiter到condition queue中,這個(gè)新的Node的waitStatus會(huì)被標(biāo)記為CONDITION Node node = addConditionWaiter(); //釋放當(dāng)前線(xiàn)程擁有的鎖,即從sync queue中去掉當(dāng)前線(xiàn)程 int savedState = fullyRelease(node); int interruptMode = 0; while (!isOnSyncQueue(node)) { //如果當(dāng)前線(xiàn)程不在持有鎖的隊(duì)列里頭,對(duì)他進(jìn)行休眠,當(dāng)其它線(xiàn)程執(zhí)行 unlock的時(shí)候,釋放鎖,就會(huì)執(zhí)行unpark操作,此時(shí)它會(huì)被喚醒,喚醒后,如果它在syn隊(duì)列里頭,開(kāi)始繼續(xù)往下執(zhí)行。(這個(gè)插入操作則是由signal完成) LockSupport.park(this); if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) break;//等待的過(guò)程中線(xiàn)程中斷了,退出 } //重新競(jìng)爭(zhēng)鎖,相當(dāng)于執(zhí)行了lock操作 if (acquireQueued(node, savedState) && interruptMode != THROW_IE) //再次去獲取鎖,如果當(dāng)前的線(xiàn)程在park的時(shí)候是被中斷了,并且ConditionObject并不是由于中斷返回,這里再次標(biāo)記為中斷 interruptMode = REINTERRUPT; if (node.nextWaiter != null) //清除非Condition模式的線(xiàn)程,而在signal中有先關(guān)操作將conditon的線(xiàn)程設(shè)置成非condition unlinkCancelledWaiters(); if (interruptMode != 0) //上報(bào)等待的過(guò)程中發(fā)生了中斷,如果是要拋出中斷,就拋出,否則再次執(zhí)行中斷 reportInterruptAfterWait(interruptMode); }
isOnSyncQueue源碼如下
final boolean isOnSyncQueue(Node node) { if (node.waitStatus == Node.CONDITION || node.prev == null) //node本身是調(diào)用了 await 方法,或者沒(méi)有在獲取鎖的隊(duì)列里頭,[如果在里頭必定有一個(gè)前置的節(jié)點(diǎn)] return false; if (node.next != null) //當(dāng)前節(jié)點(diǎn)存在下一個(gè)節(jié)點(diǎn),那么它肯定是執(zhí)行過(guò) enq ,即獲取過(guò)鎖 return true; // CAS失敗的時(shí)候,有可能 node.rev是沒(méi)有的,因此需要從頭到尾遍歷一次 return findNodeFromTail(node); }
checkInterruptWhileWaiting源碼如下
private int checkInterruptWhileWaiting(Node node) { return Thread.interrupted() ? (transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) : 0; } final boolean transferAfterCancelledWait(Node node) { if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) { //線(xiàn)程中斷重新獲取鎖,并且設(shè)置waitStatus為0,以便后續(xù)線(xiàn)程從condition queue清除 enq(node); return true; } while (!isOnSyncQueue(node)) //如果CAS失敗,只要當(dāng)前節(jié)點(diǎn)沒(méi)有在Sync queue中,那么一直自旋,每次都會(huì)交出執(zhí)行權(quán)限 Thread.yield(); return false; }
可以看到,await其實(shí)就是釋放線(xiàn)程原有的鎖,并把它放入conditon隊(duì)列中,然后執(zhí)行阻塞。等喚醒的時(shí)候,重新獲取鎖,并清掉condition queue中的線(xiàn)程。 至此await執(zhí)行結(jié)束
singnal的原理public final void signal() { if (!isHeldExclusively()) //只有當(dāng)前線(xiàn)程持有了鎖,才能釋放 throw new IllegalMonitorStateException(); Node first = firstWaiter; if (first != null) doSignal(first);//優(yōu)先釋放隊(duì)列頭的,也就是等待時(shí)間最長(zhǎng)的condition node } private void doSignal(Node first) { do { if ( (firstWaiter = first.nextWaiter) == null) lastWaiter = null; first.nextWaiter = null; } while (!transferForSignal(first) && (first = firstWaiter) != null); } //將節(jié)點(diǎn)從condition queue轉(zhuǎn)移到sync queue final boolean transferForSignal(Node node) { if (!compareAndSetWaitStatus(node, Node.CONDITION, 0)) return false; //設(shè)置為非等待失敗,則不繼續(xù)轉(zhuǎn)移 //CAS設(shè)置等待狀態(tài)為0成功 Node p = enq(node); //新節(jié)點(diǎn)放入sync queue,并返回原來(lái)的尾部節(jié)點(diǎn),也就是新節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn) int ws = p.waitStatus; if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL)) //參考shouldParkAfterFailedAcquire LockSupport.unpark(node.thread);//如果當(dāng)前節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)線(xiàn)程已經(jīng)取消,或者將當(dāng)前節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)線(xiàn)程的waitStatus設(shè)置成SIGNAL失敗,則直接喚醒當(dāng)前線(xiàn)程 return true; }
可以看到signal最關(guān)鍵的信息就是去掉等待隊(duì)列中的CONDITION狀態(tài),并將線(xiàn)程加入sync隊(duì)列,至此signal結(jié)束
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/72416.html
摘要:表示的是兩個(gè),當(dāng)其中任意一個(gè)計(jì)算完并發(fā)編程之是線(xiàn)程安全并且高效的,在并發(fā)編程中經(jīng)常可見(jiàn)它的使用,在開(kāi)始分析它的高并發(fā)實(shí)現(xiàn)機(jī)制前,先講講廢話(huà),看看它是如何被引入的。電商秒殺和搶購(gòu),是兩個(gè)比較典型的互聯(lián)網(wǎng)高并發(fā)場(chǎng)景。 干貨:深度剖析分布式搜索引擎設(shè)計(jì) 分布式,高可用,和機(jī)器學(xué)習(xí)一樣,最近幾年被提及得最多的名詞,聽(tīng)名字多牛逼,來(lái),我們一步一步來(lái)?yè)羝魄皟蓚€(gè)名詞,今天我們首先來(lái)說(shuō)說(shuō)分布式。 探究...
摘要:表示的是兩個(gè),當(dāng)其中任意一個(gè)計(jì)算完并發(fā)編程之是線(xiàn)程安全并且高效的,在并發(fā)編程中經(jīng)常可見(jiàn)它的使用,在開(kāi)始分析它的高并發(fā)實(shí)現(xiàn)機(jī)制前,先講講廢話(huà),看看它是如何被引入的。電商秒殺和搶購(gòu),是兩個(gè)比較典型的互聯(lián)網(wǎng)高并發(fā)場(chǎng)景。 干貨:深度剖析分布式搜索引擎設(shè)計(jì) 分布式,高可用,和機(jī)器學(xué)習(xí)一樣,最近幾年被提及得最多的名詞,聽(tīng)名字多牛逼,來(lái),我們一步一步來(lái)?yè)羝魄皟蓚€(gè)名詞,今天我們首先來(lái)說(shuō)說(shuō)分布式。 探究...
摘要:表示的是兩個(gè),當(dāng)其中任意一個(gè)計(jì)算完并發(fā)編程之是線(xiàn)程安全并且高效的,在并發(fā)編程中經(jīng)常可見(jiàn)它的使用,在開(kāi)始分析它的高并發(fā)實(shí)現(xiàn)機(jī)制前,先講講廢話(huà),看看它是如何被引入的。電商秒殺和搶購(gòu),是兩個(gè)比較典型的互聯(lián)網(wǎng)高并發(fā)場(chǎng)景。 干貨:深度剖析分布式搜索引擎設(shè)計(jì) 分布式,高可用,和機(jī)器學(xué)習(xí)一樣,最近幾年被提及得最多的名詞,聽(tīng)名字多牛逼,來(lái),我們一步一步來(lái)?yè)羝魄皟蓚€(gè)名詞,今天我們首先來(lái)說(shuō)說(shuō)分布式。 探究...
摘要:開(kāi)始獲取鎖終于輪到出場(chǎng)了,的調(diào)用過(guò)程和完全一樣,同樣拿不到鎖,然后加入到等待隊(duì)列隊(duì)尾然后,在阻塞前需要把前驅(qū)結(jié)點(diǎn)的狀態(tài)置為,以確保將來(lái)可以被喚醒至此,的執(zhí)行也暫告一段落了安心得在等待隊(duì)列中睡覺(jué)。 showImg(https://segmentfault.com/img/remote/1460000016012467); 本文首發(fā)于一世流云的專(zhuān)欄:https://segmentfault...
摘要:作者畢來(lái)生微信鎖狀態(tài)轉(zhuǎn)換分類(lèi)以后幫助我們提供了線(xiàn)程同步機(jī)制,通過(guò)顯示定義同步鎖來(lái)實(shí)現(xiàn)對(duì)象之間的同步。等待重新嘗試因?yàn)樵谥惺怯藐P(guān)鍵字聲明的,故可以在線(xiàn)程間可見(jiàn)再次判斷一下能否持有鎖可能線(xiàn)程同步代碼執(zhí)行得比較快,已經(jīng)釋放了鎖,不可以就返回。 作者 : 畢來(lái)生微信: 878799579 鎖狀態(tài)轉(zhuǎn)換 showImg(https://segmentfault.com/img/remote/...
閱讀 1297·2021-11-04 16:09
閱讀 3485·2021-10-19 11:45
閱讀 2396·2021-10-11 10:59
閱讀 1010·2021-09-23 11:21
閱讀 2762·2021-09-22 10:54
閱讀 1129·2019-08-30 15:53
閱讀 2600·2019-08-30 15:53
閱讀 3477·2019-08-30 12:57