AQS

AQS原理和用法:http://ifeve.com/introduce-abstractqueuedsynchronizer/
AQS源碼分析:http://cmsblogs.com/?p=2205
AQS源碼分析:https://zhuanlan.zhihu.com/p/38010971
ConditionObject源碼:http://www.reibang.com/p/4d4c7398e187

CLH鎖的基本原理

使用FIFO隊(duì)列保證公平性
有當(dāng)前節(jié)點(diǎn)和前置節(jié)點(diǎn)赁濒,當(dāng)前節(jié)點(diǎn)不斷自旋鹦付,查詢(監(jiān)聽)前置節(jié)點(diǎn)的狀態(tài)(isLocked)(保證了FIFO)
一系列的前置節(jié)點(diǎn)和當(dāng)前節(jié)點(diǎn)構(gòu)成隊(duì)列
當(dāng)前節(jié)點(diǎn)運(yùn)行完成后,更改自己的狀態(tài)试伙,那監(jiān)聽當(dāng)前節(jié)點(diǎn)狀態(tài)的線程就會(huì)結(jié)束自旋

節(jié)點(diǎn)狀態(tài)

1、CANCELLED睹逃,值為1 牙咏。場(chǎng)景:當(dāng)該線程等待超時(shí)或者被中斷,需要從同步隊(duì)列中取消等待做院,則該線程被置1盲泛,即被取消(這里該線程在取消之前是等待狀態(tài))。節(jié)點(diǎn)進(jìn)入了取消狀態(tài)則不再變化键耕;
2寺滚、SIGNAL,值為-1屈雄。當(dāng)前節(jié)點(diǎn)正在等待一個(gè)release的信號(hào)村视;
3、CONDITION酒奶,值為-2蚁孔。在等待隊(duì)列中值為-2,當(dāng)轉(zhuǎn)移到同步隊(duì)列時(shí)候惋嚎,-2改為0杠氢;
4、PROPAGATE另伍,值為-3鼻百。場(chǎng)景:表示下一次的共享狀態(tài)會(huì)被無條件的傳播下去;
5、INITIAL温艇,值為0因悲,初始狀態(tài)。

共享鎖代碼分析

共享鎖獲取

public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }

1中贝、如果獲取資源成功囤捻,則不阻塞,代碼繼續(xù)往后走邻寿。
2蝎土、doAcquireShared方法詳細(xì)見下
3、tryAcquireShared是非阻塞的绣否。<0 獲取資源失斕苎摹;0:獲取資源成功蒜撮,后續(xù)節(jié)點(diǎn)不可能成功暴构;>1:獲取資源成功,且后續(xù)節(jié)點(diǎn)獲取資源也可能成功段磨。
4取逾、共享鎖允許多個(gè)線程持有同一個(gè)鎖,所以喚醒時(shí)候需要進(jìn)行傳播苹支,以便喚醒全部等待線程砾隅。

private void doAcquireShared(int arg) {
        final Node node = addWaiter(Node.SHARED); //把節(jié)點(diǎn)加入同步隊(duì)列的隊(duì)尾
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {  // 自旋退出條件:前驅(qū)節(jié)點(diǎn)是頭節(jié)點(diǎn),并且try成功债蜜。本線程由前驅(qū)節(jié)點(diǎn)線程進(jìn)行unpark
                        setHeadAndPropagate(node, r);//刪除前驅(qū)節(jié)點(diǎn)晴埂,把當(dāng)前節(jié)點(diǎn)設(shè)置為head,如果滿足propagate寻定,則調(diào)用doReleaseShared
                        p.next = null; // help GC
                        if (interrupted)
                            selfInterrupt();
                        failed = false;
                        return;
                    }
                }
                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) //前驅(qū)節(jié)點(diǎn)正在等待信號(hào)儒洛,所以當(dāng)前節(jié)點(diǎn)必然也要等待
            return true;
        if (ws > 0) {  //前驅(qū)節(jié)點(diǎn)已經(jīng)被取消
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;//刪除所有已取消的節(jié)點(diǎn),并繼續(xù)自旋
        } else { //ws=0,-2,-3
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL); //設(shè)置前驅(qū)節(jié)點(diǎn)為signal狼速,并繼續(xù)自旋
        }
        return false;
    }

共享鎖的釋放

public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }
private void doReleaseShared() {
        for (;;) {
            Node h = head;
            if (h != null && h != tail) { //如果隊(duì)列為空則直接退出
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) //重置頭結(jié)點(diǎn)狀態(tài)為0
                        continue;            
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // 頭節(jié)點(diǎn)變化琅锻,說明有線程已經(jīng)喚醒,可以退出循環(huán)
                break;
        }
    }
private void unparkSuccessor(Node node) { //傳入的是頭節(jié)點(diǎn)
        int ws = node.waitStatus;
        if (ws < 0) //再次設(shè)置確保頭結(jié)點(diǎn)為0
            compareAndSetWaitStatus(node, ws, 0);
        Node s = node.next; //獲取待釋放的節(jié)點(diǎn)
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)  //參考https://www.zhihu.com/question/50724462
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            LockSupport.unpark(s.thread);
    }

為什么unparkSuccessor在后續(xù)節(jié)點(diǎn)斷裂向胡,從tail開始查找浅浮,原因在于enq方法插入時(shí):新節(jié)點(diǎn)pre指向tail,tail指向新節(jié)點(diǎn)捷枯,這里后繼指向前驅(qū)的指針是由CAS操作保證線程安全的滚秩。而cas操作之后t.next=node之前,可能會(huì)有其他線程進(jìn)來淮捆。所以出現(xiàn)了問題郁油,從尾部向前遍歷是一定能遍歷到所有的節(jié)點(diǎn)本股。

獨(dú)占鎖分析

獨(dú)占鎖獲取

public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

1、acquireQueued類似doAcquireShared
2桐腌、

final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

獨(dú)占鎖釋放

public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h); //獨(dú)占鎖不需要考慮共享鎖的級(jí)聯(lián)解鎖問題
            return true;
        }
        return false;
    }

條件變量CondtionObject

await和signal配合操作原理

public final void await() throws InterruptedException {
            if (Thread.interrupted())
                throw new InterruptedException();
            Node node = addConditionWaiter();
            int savedState = fullyRelease(node);
            int interruptMode = 0;
            while (!isOnSyncQueue(node)) {
                LockSupport.park(this);
                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                    break;
            }
            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
                interruptMode = REINTERRUPT;
            if (node.nextWaiter != null) // clean up if cancelled
                unlinkCancelledWaiters();
            if (interruptMode != 0)
                reportInterruptAfterWait(interruptMode);
        }

1拄显、構(gòu)造等待節(jié)點(diǎn),并加入等待隊(duì)列案站;
2躬审、釋放當(dāng)前持有的排他鎖;
3蟆盐、如果不在同步隊(duì)列中承边,則進(jìn)行park,掛起線程石挂;
4博助、當(dāng)調(diào)用signal后,會(huì)把節(jié)點(diǎn)加入同步隊(duì)列痹愚,并喚醒a(bǔ)wait中的的線程富岳;
5、await在park后繼續(xù)運(yùn)行拯腮,退出自旋窖式,并嘗試強(qiáng)鎖;
6动壤、得到鎖后運(yùn)行后續(xù)程序萝喘。

final boolean transferForSignal(Node node) {
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
            return false;
        Node p = enq(node);
        int ws = p.waitStatus;
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            LockSupport.unpark(node.thread);
        return true;
    }

acquire取消

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市狼电,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌弦蹂,老刑警劉巖肩碟,帶你破解...
    沈念sama閱讀 206,723評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異凸椿,居然都是意外死亡削祈,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門脑漫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來髓抑,“玉大人,你說我怎么就攤上這事优幸《峙模” “怎么了?”我有些...
    開封第一講書人閱讀 152,998評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵网杆,是天一觀的道長(zhǎng)羹饰。 經(jīng)常有香客問我伊滋,道長(zhǎng),這世上最難降的妖魔是什么队秩? 我笑而不...
    開封第一講書人閱讀 55,323評(píng)論 1 279
  • 正文 為了忘掉前任笑旺,我火速辦了婚禮,結(jié)果婚禮上馍资,老公的妹妹穿的比我還像新娘筒主。我一直安慰自己,他們只是感情好鸟蟹,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評(píng)論 5 374
  • 文/花漫 我一把揭開白布乌妙。 她就那樣靜靜地躺著,像睡著了一般戏锹。 火紅的嫁衣襯著肌膚如雪冠胯。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,079評(píng)論 1 285
  • 那天锦针,我揣著相機(jī)與錄音荠察,去河邊找鬼。 笑死奈搜,一個(gè)胖子當(dāng)著我的面吹牛悉盆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播馋吗,決...
    沈念sama閱讀 38,389評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼焕盟,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了宏粤?” 一聲冷哼從身側(cè)響起脚翘,我...
    開封第一講書人閱讀 37,019評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎绍哎,沒想到半個(gè)月后来农,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡崇堰,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評(píng)論 2 325
  • 正文 我和宋清朗相戀三年沃于,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片海诲。...
    茶點(diǎn)故事閱讀 38,100評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡繁莹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出特幔,到底是詐尸還是另有隱情咨演,我是刑警寧澤,帶...
    沈念sama閱讀 33,738評(píng)論 4 324
  • 正文 年R本政府宣布蚯斯,位于F島的核電站雪标,受9級(jí)特大地震影響零院,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜村刨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評(píng)論 3 307
  • 文/蒙蒙 一告抄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧嵌牺,春花似錦打洼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至僻弹,卻和暖如春阿浓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蹋绽。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評(píng)論 1 262
  • 我被黑心中介騙來泰國打工芭毙, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人卸耘。 一個(gè)月前我還...
    沈念sama閱讀 45,547評(píng)論 2 354
  • 正文 我出身青樓退敦,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親蚣抗。 傳聞我的和親對(duì)象是個(gè)殘疾皇子侈百,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評(píng)論 2 345

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