由淺入深ReentrantLock源碼閱讀

重入鎖ReentrantLock险掀,顧名思義,就是支持重進(jìn)入的鎖艇抠,它表示該鎖能夠支持一個(gè)線程對資源的重復(fù)加鎖。除此之外炭剪,該鎖的還支持獲取鎖時(shí)的公平和非公平性選擇练链。

閱讀這個(gè)可重入鎖類之前翔脱,可以先閱讀我的上兩篇文章奴拦,對lock以及AbstractQueuedSynchronizer這兩個(gè)類的作用和設(shè)計(jì)有一個(gè)基礎(chǔ)的了解。然后再看著個(gè)類的時(shí)候届吁,會更好的理解错妖。
AQS(AbstractQueuedSynchronizer)隊(duì)列同步器源碼閱讀(二)
http://www.reibang.com/p/e0066f9349cd
AQS(AbstractQueuedSynchronizer)隊(duì)列同步器源碼閱讀(一)
http://www.reibang.com/p/a41088fc1516

然后我們可以知道一般來說,ReentrantLock是一個(gè)可重入鎖疚沐,所以會實(shí)現(xiàn)Lock這個(gè)類暂氯,并重寫該類提供的幾個(gè)方法:
//獲取鎖,釋放鎖
void lock();
void unlock();
//可相應(yīng)線程中斷的獲取鎖亮蛔,當(dāng)線程被中斷后痴施,鎖會被釋放。而一般的lock則不會響應(yīng)
void lockInterruptibly() throws InterruptedException;
//嘗試獲取鎖究流,獲取不到則返回false
boolean tryLock();
//與上一個(gè)一樣
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
//獲取對應(yīng)的condition辣吃,后面會專門研究,現(xiàn)在只要知道芬探,這個(gè)可以用來配合完成不同線程的等待/通知機(jī)制神得。
Condition newCondition();

然后一般重寫以上幾個(gè)方法時(shí),需要依賴隊(duì)列同步器來獲取同步狀態(tài)偷仿,獲取不到需要加入同步隊(duì)列等等哩簿,所以一般還會設(shè)計(jì)工具類Sync繼承AbstractQueuedSynchronizer

然后根據(jù)設(shè)計(jì)不同的鎖去重寫AQS提供的一些方法。

繼承AQS的子類重寫以上AQS提供的方法后酝静,對外ReentrantLock是實(shí)現(xiàn)Lock提供的幾個(gè)方法一般是調(diào)用Sync繼承的AQS里面的方法节榜。例如:

加鎖時(shí):

//調(diào)用的是AQS的子類sync的lock
public void lock() {
        sync.lock();
    }
//而lock方法一般會根據(jù)具體的鎖的設(shè)計(jì)去實(shí)現(xiàn)我們已經(jīng)重寫的acquire方法。大體的鎖的設(shè)計(jì)不會偏差太多别智。

然后我們來具體看一下:
ReentrantLock 是一個(gè)可重入鎖宗苍。
關(guān)于線程的調(diào)度策略分為公平調(diào)度和非公平調(diào)度。
他的設(shè)計(jì)同樣是內(nèi)部有一個(gè)繼承AQS的靜態(tài)內(nèi)部類Sync亿遂,同時(shí)為了區(qū)分不同的調(diào)度策略浓若,有設(shè)計(jì)了兩個(gè)子類繼承Sync渺杉,

static final class NonfairSync extends Sync{.....}
static final class FairSync extends Sync{.....}

看類名可以看出,一個(gè)是針對非公平調(diào)度策略設(shè)計(jì)的鎖挪钓,一個(gè)是公平調(diào)度的是越。兩種重寫同步隊(duì)列器的方法的實(shí)現(xiàn)不同。

再來看構(gòu)造方法:

public ReentrantLock() {
        sync = new NonfairSync();
    }

    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

這回應(yīng)該不用多解釋了吧碌上,看類名倚评。所以說可重入鎖是默認(rèn)非公平調(diào)度策略的,就是說線程執(zhí)行的順序是不是按執(zhí)行時(shí)間順序執(zhí)行的馏予,是非公平的天梧,效率比較高。

可重入性

可重入鎖顧名思義就是線程獲取到鎖之后霞丧,可以再次獲取該鎖呢岗,而不會被鎖阻塞。該鎖實(shí)現(xiàn)需要解決兩個(gè)問題:

1.線程再次獲取鎖蛹尝。鎖需要去識別獲取鎖的線程是否為當(dāng)前占據(jù)鎖的線程后豫,如果是,則再次成功獲取突那。

2.鎖的最終釋放挫酿。。線程重復(fù)n次獲取了鎖愕难,隨后在第n次釋放該鎖后早龟,其他線程能夠獲取到該鎖。

以默認(rèn)的非公平調(diào)度的鎖實(shí)現(xiàn)來查看獲取同步狀態(tài)時(shí)猫缭,如何處理:

 final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            //獲取當(dāng)前state葱弟,重入數(shù)量
            int c = getState();
            //如果還未有線程獲取該鎖
            if (c == 0) {
            //獲取同步狀態(tài)成功,則設(shè)置當(dāng)前線程為持有鎖的線程
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //如果已有線程獲取該鎖饵骨,且該線程為已持有鎖的線程
            else if (current == getExclusiveOwnerThread()) {
              //設(shè)置重入的數(shù)量翘悉。并返回獲取鎖成功
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

該方法增加了再次獲取同步狀態(tài)的處理邏輯:通過判斷當(dāng)前線程是否為獲取鎖的線程來決定獲取操作是否成功,如果是獲取鎖的線程再次請求居触,則將同步狀態(tài)值進(jìn)行增加并返回true妖混,表示獲取同步狀態(tài)成功。

成功獲取鎖的線程再次獲取鎖轮洋,只是增加了同步狀態(tài)值制市,這也就要求ReentrantLock在釋放同步狀態(tài)時(shí)減少同步狀態(tài)值。

protected final boolean tryRelease(int releases) {
//釋放時(shí)減去同步狀態(tài)值
            int c = getState() - releases;
            //如果當(dāng)前線程與持有線程不一致弊予,則報(bào)對象頭狀態(tài)異常
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
          //如果同步狀態(tài)值為0祥楣,代表該鎖已全部釋放,需要釋放鎖,使其他線程能夠獲取該鎖
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

如果該鎖被獲取了n次误褪,那么前(n-1)次tryRelease(int releases)方法必須返回false责鳍,而只有同步狀態(tài)完全釋放了,才能返回true兽间±穑可以看到,該方法將同步狀態(tài)是否為0作為最終釋放的條件嘀略,當(dāng)同步狀態(tài)為0時(shí)恤溶,將占有線程設(shè)置為null,并返回true帜羊,表示釋放成功咒程。

對于非公平鎖,只要CAS設(shè)置同步狀態(tài)成功讼育,則表示當(dāng)前線程獲取了鎖帐姻,而公平鎖則不同.

protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
//主要區(qū)別如下: 即加入了同步隊(duì)列中當(dāng)前節(jié)點(diǎn)是否有前驅(qū)節(jié)點(diǎn)的判斷,如果該方法返回true窥淆,
//則表示有線程比當(dāng)前線程更早地請求獲取鎖卖宠,因此需要等待前驅(qū)線程獲取并釋放鎖之后才能繼續(xù)獲取鎖。
                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;

非公平調(diào)度策略加鎖的流程

final void lock() {

//1.如果同步操作state忧饭,獲取同步狀態(tài)成功,則設(shè)置當(dāng)前線程為當(dāng)前獨(dú)占式獲取鎖.否則進(jìn)行獲取筷畦。

//compareAndSetState(0, 1)  使用的是:
//unsafe.compareAndSwapInt(this, stateOffset, expect, update);  CAS偏移地址來直接修改state的值词裤。
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                // 2.否則獲取同步狀態(tài)。進(jìn)去里面看實(shí)現(xiàn)
               acquire(1);
        }

//1.產(chǎn)時(shí)獲取同步狀態(tài)鳖宾,(tryAcquire),  
失敗則加入隊(duì)尾tail吼砂,狀態(tài)設(shè)置為EXCLUSIVE,(addWaiter(Node.EXCLUSIVE), arg))
同時(shí)進(jìn)入自旋去獲取同步狀態(tài)鼎文,直到該節(jié)點(diǎn)前一個(gè)節(jié)點(diǎn)為頭節(jié)點(diǎn)并獲取成功渔肩,則出隊(duì)列,并喚醒下一個(gè)節(jié)點(diǎn)拇惋,并且響應(yīng)中斷周偎。(acquireQueued()
public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

tryAcquire()看上面的分析.主要調(diào)用我們NonfairSync重寫的nonfairTryAcquire。
如果獲取非公平的可重入鎖失敗撑帖,則執(zhí)行下面方法蓉坎。該方法不做具體分析了,之前已經(jīng)有具體分析過了胡嘿。
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);
        }
}

非公平調(diào)度策略釋放鎖的流程就不再說了,上面有勿侯。

然后本來想要再分享下拓瞪,公平調(diào)度獲取鎖和解鎖的過程,其實(shí)不用助琐。解鎖的過程是一致的吴藻,這個(gè)前面有說了。然后加鎖的區(qū)別也是只有一點(diǎn)區(qū)別弓柱。就是獲取同步狀態(tài)的時(shí)候沟堡,需要判斷前面是否有頭節(jié)點(diǎn),如果沒有則可以直接獲取同步狀態(tài)矢空,否則繼續(xù)自旋航罗。因?yàn)楣秸{(diào)度是根據(jù)時(shí)間線程執(zhí)行的時(shí)間順序獲取鎖的,所以通過控制這點(diǎn)屁药,來判斷是否按時(shí)間進(jìn)行獲取同步狀態(tài)粥血,從而控制獲取鎖的順序。

從上面### 標(biāo)記的那行開始看酿箭,以下代碼的邏輯與nonfairTryAcquire基本上一直复亏,唯一的不同在于增加了hasQueuedPredecessors的邏輯判斷,方法名就可知道該方法用來判斷當(dāng)前節(jié)點(diǎn)在同步隊(duì)列中是否有前驅(qū)節(jié)點(diǎn)的判斷缭嫡,如果有前驅(qū)節(jié)點(diǎn)說明有線程比當(dāng)前線程更早的請求資源缔御,根據(jù)公平性,當(dāng)前線程請求資源失敗妇蛀。如果當(dāng)前節(jié)點(diǎn)沒有前驅(qū)節(jié)點(diǎn)的話耕突,再才有做后面的邏輯判斷的必要性。公平鎖每次都是從同步隊(duì)列中的第一個(gè)節(jié)點(diǎn)獲取到鎖评架,而非公平性鎖則不一定眷茁,有可能剛釋放鎖的線程能再次獲取到鎖。

公平鎖 VS 非公平鎖

1.公平鎖每次獲取到鎖為同步隊(duì)列中的第一個(gè)節(jié)點(diǎn)纵诞,保證請求資源時(shí)間上的絕對順序上祈,而非公平鎖有可能剛釋放鎖的線程下次繼續(xù)獲取該鎖,則有可能導(dǎo)致其他線程永遠(yuǎn)無法獲取到鎖浙芙,造成“饑餓”現(xiàn)象登刺。

2.公平鎖為了保證時(shí)間上的絕對順序,需要頻繁的上下文切換茁裙,而非公平鎖會降低一定的上下文切換塘砸,降低性能開銷。因此晤锥,ReentrantLock默認(rèn)選擇的是非公平鎖掉蔬,則是為了減少一部分上下文切換廊宪,保證了系統(tǒng)更大的吞吐量。

編寫不易女轿,給個(gè)贊

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末箭启,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蛉迹,更是在濱河造成了極大的恐慌傅寡,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件北救,死亡現(xiàn)場離奇詭異荐操,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)珍策,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進(jìn)店門托启,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人攘宙,你說我怎么就攤上這事屯耸。” “怎么了蹭劈?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵疗绣,是天一觀的道長。 經(jīng)常有香客問我铺韧,道長多矮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任祟蚀,我火速辦了婚禮工窍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘前酿。我一直安慰自己,他們只是感情好鹏溯,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布罢维。 她就那樣靜靜地躺著,像睡著了一般丙挽。 火紅的嫁衣襯著肌膚如雪肺孵。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天颜阐,我揣著相機(jī)與錄音平窘,去河邊找鬼。 笑死凳怨,一個(gè)胖子當(dāng)著我的面吹牛瑰艘,可吹牛的內(nèi)容都是我干的是鬼。 我是一名探鬼主播,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼紫新,長吁一口氣:“原來是場噩夢啊……” “哼均蜜!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起芒率,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤囤耳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后偶芍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體充择,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年匪蟀,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了椎麦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,622評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡萄窜,死狀恐怖铃剔,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情查刻,我是刑警寧澤键兜,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站穗泵,受9級特大地震影響普气,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜佃延,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一现诀、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧履肃,春花似錦仔沿、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至膘螟,卻和暖如春成福,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背荆残。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工奴艾, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人内斯。 一個(gè)月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓蕴潦,卻偏偏與公主長得像像啼,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子品擎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內(nèi)容