摘要:概述前面已經(jīng)講解了關(guān)于的非公平鎖模式,關(guān)于非公平鎖,內(nèi)部其實(shí)告訴我們誰(shuí)先爭(zhēng)搶到鎖誰(shuí)就先獲得資源,下面就來(lái)分析一下公平鎖內(nèi)部是如何實(shí)現(xiàn)公平的如果沒(méi)有看過(guò)非公平鎖的先去了解下非公平鎖,因?yàn)檫@篇文章前面不會(huì)講太多內(nèi)部結(jié)構(gòu),直接會(huì)對(duì)源碼進(jìn)行分析前文
概述
前面已經(jīng)講解了關(guān)于AQS的非公平鎖模式,關(guān)于NonfairSync非公平鎖,內(nèi)部其實(shí)告訴我們誰(shuí)先爭(zhēng)搶到鎖誰(shuí)就先獲得資源,下面就來(lái)分析一下公平鎖FairSync內(nèi)部是如何實(shí)現(xiàn)公平的?如果沒(méi)有看過(guò)非公平鎖的先去了解下非公平鎖,因?yàn)檫@篇文章前面不會(huì)講太多內(nèi)部結(jié)構(gòu),直接會(huì)對(duì)源碼進(jìn)行分析
前文連接地址:圖解AQS原理之ReentrantLock詳解-非公平鎖
本文分析的JDK版本是1.8源碼分析溫馨提示:讀本文內(nèi)容建議結(jié)合之前寫的非公平,前篇設(shè)計(jì)了很多基礎(chǔ)性內(nèi)容
在源碼分析之前,我們先來(lái)看一下ReentrantLock如何切換獲取鎖的模式呢?其實(shí)是在構(gòu)造器中傳遞指定的類型變量來(lái)控制使用鎖的方式,如下所示:
public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
當(dāng)fair參數(shù)指定為true時(shí),代表的是公平鎖,如果指定為false則使用的非公平,無(wú)參的構(gòu)造函數(shù)默認(rèn)使用的是非公平模式,如下所示:
public ReentrantLock() { sync = new NonfairSync(); }
接下來(lái)我們以一個(gè)例子來(lái)進(jìn)行后面的說(shuō)明:
public class ReentrantLockDemo { public static void main(String[] args) throws Exception { AddDemo runnalbeDemo = new AddDemo(); Thread thread = new Thread(runnalbeDemo::add); thread.start(); Thread.sleep(500); Thread thread1 = new Thread(runnalbeDemo::add); thread1.start(); System.out.println(runnalbeDemo.getCount()); } private static class AddDemo { private final AtomicInteger count = new AtomicInteger(); private final ReentrantLock reentrantLock = new ReentrantLock(true); private final Condition condition = reentrantLock.newCondition(); private void add() { try { reentrantLock.lockInterruptibly(); count.getAndIncrement(); } catch (Exception ex) { System.out.println("線程被中斷了"); } finally { // reentrantLock.unlock(); } } int getCount() { return count.get(); } } }
我們通過(guò)源碼可以看到這里我們啟動(dòng)了兩個(gè)線程,兩個(gè)線程分別進(jìn)行同步鎖操作,這里我并沒(méi)有釋放掉鎖,因?yàn)榉奖惴治鲫?duì)列的情況,當(dāng)然你也可以在內(nèi)部寫一個(gè)死循環(huán),不釋放鎖就可以了,我這里簡(jiǎn)單的不釋放鎖,使用的是可中斷的獲取鎖操作方法lockInterruptibly,這里內(nèi)部的原理我們上一篇文章中已經(jīng)講解過(guò)了,這里并不過(guò)多的去分析內(nèi)部原理,這個(gè)ReentrantLock的lockInterruptibly調(diào)用內(nèi)部類AQS的acquireInterruptibly,但是其實(shí)是FairSync內(nèi)部類繼承了內(nèi)部類Sync,而內(nèi)部類Sync有繼承了AbstractQueuedSynchronizer簡(jiǎn)稱AQS,acquireInterruptibly源碼信息如下所示:
public final void acquireInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); if (!tryAcquire(arg)) doAcquireInterruptibly(arg); }
這里我們通過(guò)上一篇文章得知tryAcquire是需要子類去實(shí)現(xiàn)的方法,我們?cè)诶又兄付耸褂玫氖枪芥i,所以tryAcquire方法的實(shí)現(xiàn)是在ReentrentLock的FairSync類中,我們來(lái)具體看一下這個(gè)方法,重點(diǎn)也在這個(gè)方法中其他的其實(shí)都是一樣的,因?yàn)橛玫姆椒ǘ紩?huì)一樣的非公平和公平鎖的調(diào)用,唯獨(dú)不一樣的就是子類實(shí)現(xiàn)的方法是不相同的,接下來(lái)我們就來(lái)看一下公平鎖的tryAcquire是如何實(shí)現(xiàn)的?
protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (!hasQueuedPredecessors() && //判斷是否有等待的線程在隊(duì)列中 compareAndSetState(0, acquires)) { //嘗試爭(zhēng)搶鎖操作 setExclusiveOwnerThread(current); //設(shè)置當(dāng)前線程獨(dú)占鎖資源 return true; //獲得鎖成功 } } else if (current == getExclusiveOwnerThread()) { //當(dāng)前線程和獨(dú)占鎖資源的線程一致,則可以重入 int nextc = c + acquires; //state遞增 if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); //設(shè)置state狀態(tài) return true; //獲得鎖成功 } return false; //獲得鎖失敗 }
對(duì)比非公平鎖的NonfairSync類的tryAcquire方法,其實(shí)就是在鎖可用的情況下增加了一個(gè)判斷條件,這個(gè)判斷方法就是hasQueuedPredecessors,從方法的名稱來(lái)看說(shuō)的是有等待的線程隊(duì)列,換句話說(shuō)已經(jīng)有人在排隊(duì)了,新來(lái)的線程你就不能加塞,而非公平模式的誰(shuí)先爭(zhēng)搶到鎖就是誰(shuí)的,管你先來(lái)不先來(lái),接下來(lái)我們具體看一下這個(gè)
hasQueuedPredecessors方法源碼:
public final boolean hasQueuedPredecessors() { // The correctness of this depends on head being initialized // before tail and on head.next being accurate if the current // thread is first in queue. Node t = tail; // 獲得尾節(jié)點(diǎn) Node h = head; // 獲得頭節(jié)點(diǎn) Node s; return h != t && //頭節(jié)點(diǎn)和尾節(jié)點(diǎn)相同代表隊(duì)列為空 ((s = h.next) == null || s.thread != Thread.currentThread()); //頭節(jié)點(diǎn)的next節(jié)點(diǎn)為空代表頭節(jié)點(diǎn),以及s.thread不是當(dāng)前線程不是自己的話代表隊(duì)列中存在元素 }
通過(guò)上面的源碼信息,可以得出其實(shí)內(nèi)部主要就是判斷有沒(méi)有排隊(duì)等待的節(jié)點(diǎn),隊(duì)列是否為空,如果為空的話則可以爭(zhēng)搶鎖,如果隊(duì)列不為空,伙計(jì)你必須老老實(shí)實(shí)給我排隊(duì)去,除非占有鎖的線程和請(qǐng)求鎖的線程是一樣的,否則還是老老實(shí)實(shí)排隊(duì)去,這就是公平模式的鎖操作,還有一個(gè)lock方法,公平模式的lock方法,沒(méi)有直接上來(lái)先獲取鎖,而是先嘗試獲得鎖直接調(diào)用AQS的aquire方法進(jìn)行嘗試獲取鎖,下面是FairSync源碼:
static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; final void lock() { acquire(1); //這里直接調(diào)用了aquire并沒(méi)有嘗試修改state狀態(tài) } /** * Fair version of tryAcquire. Don"t grant access unless * recursive call or no waiters or is first. */ protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } }結(jié)束語(yǔ)
本內(nèi)容主要是結(jié)合上篇內(nèi)容的一個(gè)續(xù)篇,可以結(jié)合上篇然后再看下篇會(huì)比較清晰些。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/74749.html
摘要:內(nèi)部提供了兩種的實(shí)現(xiàn),一種公平模式,一種是非公平模式,如果沒(méi)有特別指定在構(gòu)造器中,默認(rèn)是非公平的模式,我們可以看一下無(wú)參的構(gòu)造函數(shù)。 概述 并發(fā)編程中,ReentrantLock的使用是比較多的,包括之前講的LinkedBlockingQueue和ArrayBlockQueue的內(nèi)部都是使用的ReentrantLock,談到它又不能的不說(shuō)AQS,AQS的全稱是AbstractQueue...
摘要:所以大家看下面的圖,就是線程跑過(guò)來(lái)加鎖的一個(gè)過(guò)程。好線程現(xiàn)在就重新嘗試加鎖,這時(shí)還是用操作將從變?yōu)?,此時(shí)就會(huì)成功,成功之后代表加鎖成功,就會(huì)將設(shè)置為。此外,還要把加鎖線程設(shè)置為線程自己,同時(shí)線程自己就從等待隊(duì)列中出隊(duì)了。 ReentrantLock和AQS的關(guān)系 ReentrantLock使用 showImg(https://segmentfault.com/img/bVbkZr4?...
摘要:的主要功能和關(guān)鍵字一致,均是用于多線程的同步。而僅支持通過(guò)查詢當(dāng)前線程是否持有鎖。由于和使用的是同一把可重入鎖,所以線程可以進(jìn)入方法,并再次獲得鎖,而不會(huì)被阻塞住。公平與非公平公平與非公平指的是線程獲取鎖的方式。 1.簡(jiǎn)介 可重入鎖ReentrantLock自 JDK 1.5 被引入,功能上與synchronized關(guān)鍵字類似。所謂的可重入是指,線程可對(duì)同一把鎖進(jìn)行重復(fù)加鎖,而不會(huì)被阻...
摘要:開(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ā)于一世流云的專欄:https://segmentfault...
摘要:公平策略在多個(gè)線程爭(zhēng)用鎖的情況下,公平策略傾向于將訪問(wèn)權(quán)授予等待時(shí)間最長(zhǎng)的線程。使用方式的典型調(diào)用方式如下二類原理的源碼非常簡(jiǎn)單,它通過(guò)內(nèi)部類實(shí)現(xiàn)了框架,接口的實(shí)現(xiàn)僅僅是對(duì)的的簡(jiǎn)單封裝,參見(jiàn)原理多線程進(jìn)階七鎖框架獨(dú)占功能剖析 showImg(https://segmentfault.com/img/remote/1460000016012582); 本文首發(fā)于一世流云的專欄:https...
閱讀 3965·2021-09-09 09:33
閱讀 1830·2021-09-06 15:14
閱讀 1955·2019-08-30 15:44
閱讀 3128·2019-08-29 18:36
閱讀 3798·2019-08-29 16:22
閱讀 2122·2019-08-29 16:21
閱讀 2572·2019-08-29 15:42
閱讀 1678·2019-08-29 11:00