AQS-獨(dú)占與共享

AQS的功能可以分為兩類:獨(dú)占與共享;如ReentrantLock利用了其獨(dú)占功能,CountDownLatch,Semaphore利用了其共享功能嗓违。
AQS的靜態(tài)內(nèi)部類Node里有兩個(gè)變量,獨(dú)占鎖與共享鎖在創(chuàng)建自己的節(jié)點(diǎn)時(shí)(addWaiter方法)用于表明身份,它們會(huì)被賦值給Node的nextWaiter變量图贸。

        static final Node SHARED = new Node();
        static final Node EXCLUSIVE = null;

獨(dú)占鎖

獨(dú)占鎖就是每次只允許一個(gè)線程執(zhí)行蹂季,當(dāng)前線程執(zhí)行完會(huì)release將同步狀態(tài)歸零,再喚醒后繼節(jié)點(diǎn)疏日,這里通過(guò)自定義tryAcquire來(lái)實(shí)現(xiàn)公平與非公平(即是否允許插隊(duì))偿洁;
acquire & release

//成功代表同步狀態(tài)的變更,排斥其他線程沟优;否則加入等待隊(duì)列
    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
//歸零同步狀態(tài)涕滋,喚醒后繼節(jié)點(diǎn)
    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

這里通過(guò)同步狀態(tài)來(lái)實(shí)現(xiàn)獨(dú)占功能。

共享鎖

如Semaphore挠阁,CountDownLatch宾肺,它們調(diào)用的是AQS里的acquireSharedInterruptibly與releaseShared;實(shí)現(xiàn)自己的tryAcquireShared與tryReleaseShared侵俗,這里便體現(xiàn)了獨(dú)占與共享的不同锨用,獨(dú)占鎖的tryAcquire,tryRelease返回boolean代表同步狀態(tài)更改的成功與否隘谣;tryAcquireShared返回int值增拥,tryAcquireShared返回0代表當(dāng)前線程能夠執(zhí)行,但之后的將會(huì)進(jìn)入等待隊(duì)列中寻歧;返回正數(shù)直接執(zhí)行掌栅,之后的線程可能也可以直接執(zhí)行;
以Semphore的非公平鎖為例熄求,如果當(dāng)前正在執(zhí)行的線程數(shù)小于限制值渣玲,就CAS更改同步狀態(tài)值逗概,線程直接執(zhí)行弟晚。返回負(fù)數(shù)代表正在執(zhí)行的線程數(shù)達(dá)到允許值。

        final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }

Semphore限制同時(shí)執(zhí)行的線程數(shù),當(dāng)一個(gè)線程acquire成功
acquireShared

    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }

    private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
//這里對(duì)中斷的處理也不同卿城,共享式的線程被喚醒后發(fā)現(xiàn)中斷標(biāo)記后枚钓,會(huì)直接拋中斷異常
//而獨(dú)占鎖采用的中斷策略是恢復(fù)中斷標(biāo)記,也就是交給調(diào)用方去處理中斷
                   throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

releaseShared
tryReleaseShared由子類實(shí)現(xiàn)瑟押,返回boolean搀捷,以Semphore為例

        protected final boolean tryReleaseShared(int releases) {
            for (;;) {
                int current = getState();
                int next = current + releases;
                if (next < current) // overflow
                    throw new Error("Maximum permit count exceeded");
                if (compareAndSetState(current, next))
                    return true;
            }
        }

當(dāng)一個(gè)線程執(zhí)行完會(huì)增加我們的同步狀態(tài)值,返回true多望,之后doReleaseShared喚醒head.next節(jié)點(diǎn)里的線程嫩舟。

    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) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }

    private void unparkSuccessor(Node node) {
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        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)
            LockSupport.unpark(s.thread);
    }

doReleaseShared在之前介紹Semaphore里說(shuō)過(guò);若有后繼節(jié)點(diǎn)就喚醒它怀偷;若沒(méi)有(即ws == 0家厌,因?yàn)閣aitStatus的值是后繼節(jié)點(diǎn)賦予的)則將head節(jié)點(diǎn)waitStatus改為PROPAGATE,之后進(jìn)入的節(jié)點(diǎn)會(huì)將此head的狀態(tài)改為SIGNAL椎工,具體實(shí)現(xiàn)在shouldParkAfterFailedAcquire里饭于。

總結(jié)

獨(dú)占與共享最大不同就在各自的tryacquire里,對(duì)于獨(dú)占來(lái)說(shuō)只有true或false维蒙,只有一個(gè)線程得以執(zhí)行任務(wù)掰吕;而對(duì)于共享鎖的tryAcquireShared來(lái)說(shuō),線程數(shù)沒(méi)達(dá)到限制都可以直接執(zhí)行颅痊。
但本質(zhì)上都是對(duì)AQS同步狀態(tài)的修改殖熟,一個(gè)是0與1之間,另一個(gè)允許更多而已斑响。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末吗讶,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子恋捆,更是在濱河造成了極大的恐慌照皆,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,525評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件沸停,死亡現(xiàn)場(chǎng)離奇詭異膜毁,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)愤钾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門瘟滨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人能颁,你說(shuō)我怎么就攤上這事杂瘸。” “怎么了伙菊?”我有些...
    開封第一講書人閱讀 164,862評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵败玉,是天一觀的道長(zhǎng)敌土。 經(jīng)常有香客問(wèn)我,道長(zhǎng)运翼,這世上最難降的妖魔是什么返干? 我笑而不...
    開封第一講書人閱讀 58,728評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮血淌,結(jié)果婚禮上矩欠,老公的妹妹穿的比我還像新娘。我一直安慰自己悠夯,他們只是感情好癌淮,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著沦补,像睡著了一般该默。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上策彤,一...
    開封第一講書人閱讀 51,590評(píng)論 1 305
  • 那天栓袖,我揣著相機(jī)與錄音,去河邊找鬼店诗。 笑死裹刮,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的庞瘸。 我是一名探鬼主播捧弃,決...
    沈念sama閱讀 40,330評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼擦囊!你這毒婦竟也來(lái)了违霞?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,244評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤瞬场,失蹤者是張志新(化名)和其女友劉穎买鸽,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贯被,經(jīng)...
    沈念sama閱讀 45,693評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡眼五,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了彤灶。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片看幼。...
    茶點(diǎn)故事閱讀 40,001評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖幌陕,靈堂內(nèi)的尸體忽然破棺而出诵姜,到底是詐尸還是另有隱情,我是刑警寧澤搏熄,帶...
    沈念sama閱讀 35,723評(píng)論 5 346
  • 正文 年R本政府宣布棚唆,位于F島的核電站暇赤,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏瑟俭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評(píng)論 3 330
  • 文/蒙蒙 一契邀、第九天 我趴在偏房一處隱蔽的房頂上張望摆寄。 院中可真熱鬧,春花似錦坯门、人聲如沸微饥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)欠橘。三九已至,卻和暖如春现恼,著一層夾襖步出監(jiān)牢的瞬間肃续,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工叉袍, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留始锚,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,191評(píng)論 3 370
  • 正文 我出身青樓喳逛,卻偏偏與公主長(zhǎng)得像瞧捌,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子润文,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評(píng)論 2 355

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