AQS詳解

解釋

AQS:全稱(chēng)“AbstractQueuedSynchronizer”,直譯過(guò)來(lái)是抽象的隊(duì)列同步器边苹,一般我們把它叫做AQS,java中大部分并發(fā)類(lèi)都是通過(guò)它來(lái)實(shí)現(xiàn)線(xiàn)程同步裁僧。它內(nèi)部定義了一個(gè)變量(volatile int state)和一個(gè)等待隊(duì)列勾给,前者表示加鎖狀態(tài),后者在多線(xiàn)程情況下?tīng)?zhēng)用資源時(shí)被阻塞會(huì)進(jìn)入等待隊(duì)列锅知。

源碼解析

volatile int state

volatile 是一個(gè)關(guān)鍵字播急,在并發(fā)處理中也經(jīng)常會(huì)用到,后面單獨(dú)用一篇文章介紹售睹。

(已去除源碼中的注釋?zhuān)奖汩喿x)

private volatile int state;
protected final int getState() {
        return state;
    }
protected final void setState(int newState) {
        state = newState;
    }
protected final boolean compareAndSetState(int expect, int update) {
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }

獨(dú)占鎖(下面會(huì)介紹)state初始為0桩警,表示未鎖定狀態(tài),A線(xiàn)程加鎖成功則state+1昌妹,這樣后面的線(xiàn)程嘗試獲取鎖的時(shí)候就會(huì)失敗捶枢,只有當(dāng)A線(xiàn)程釋放鎖state=0時(shí),后面的鎖才有可能成功獲取鎖飞崖。如果是可重入鎖烂叔,那么A線(xiàn)程再次獲取鎖的時(shí)候,state會(huì)累加固歪,當(dāng)然蒜鸡,釋放鎖也要一層一層釋放,直到state=0牢裳。共享鎖state初始為N逢防,多個(gè)線(xiàn)程可以同時(shí)執(zhí)行,每個(gè)線(xiàn)程執(zhí)行完會(huì)state-1蒲讯,直到state=0時(shí)忘朝,再調(diào)用主線(xiàn)程。

其中compareAndSetState方法判帮,是原子操作局嘁,里面使用了unsafe中的compareAndSwapInt方法溉箕,這個(gè)Unsafe(不安全)類(lèi),聽(tīng)著就不靠譜悦昵,為什么這里會(huì)用到呢约巷。其實(shí)不止在這里,在JUC(java.util.concurrent)包中旱捧,尤其是在CAS里大量的用到Unsafe独郎,這是因?yàn)镴ava不能像c語(yǔ)言那樣直接訪(fǎng)問(wèn)操作系統(tǒng)底層,但Unsafe類(lèi)提供了硬件級(jí)別的原子操作枚赡,而且有很高的效率氓癌。那為什么官方不建議開(kāi)發(fā)者使用這個(gè)類(lèi)呢,感覺(jué)特地取個(gè)這名字(Unsafe)就是嚇唬程序員贫橙,不要用贪婉。因?yàn)閁nsafe中直接訪(fǎng)問(wèn)內(nèi)存的方法中使用的內(nèi)存不受JVM管理,也就不能被GC卢肃,需要手動(dòng)管理疲迂,稍有不慎就可能導(dǎo)致內(nèi)存泄漏。

隊(duì)列操作

private Node enq(final Node node) {
    for (;;) {
        Node t = tail;
        if (t == null) { // Must initialize
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}

將節(jié)點(diǎn)插入隊(duì)列莫湘,必要時(shí)進(jìn)行初始化尤蒿。

里面的for (;;),又是個(gè)騷操作幅垮,有人會(huì)問(wèn)為啥不用while(true)呢腰池,都是無(wú)限循環(huán)忙芒,而且while(true)看著就很舒服示弓。經(jīng)常看源碼的同學(xué)呵萨,可能會(huì)發(fā)現(xiàn),jdk源碼中很多地方都是for(;;)囱皿,至于為啥用這個(gè)咆耿,咱們以后有機(jī)會(huì)再討論萨螺。

Node t = tail;(tail是尾節(jié)點(diǎn),head是頭節(jié)點(diǎn))先定義一個(gè)尾部節(jié)點(diǎn)椭盏,如果尾節(jié)點(diǎn)是空的掏颊,說(shuō)明隊(duì)列是空的,這時(shí)就需要初始化盆偿,源碼中特地加了注釋// Must initialize事扭,(這種直接在后面加注釋的乐横,咱們最好不要學(xué)罐农,不符合阿里代碼規(guī)范蛆楞,但是Doug Lea是大大佬豹爹,阿里代碼規(guī)范管不住他);初始化以后,接著循環(huán)進(jìn)入else艾君,node.prev = t;將當(dāng)前節(jié)點(diǎn)掛在尾節(jié)點(diǎn)后面冰垄;(這種node.prev指向上個(gè)節(jié)點(diǎn)逝薪,t.next指向下個(gè)節(jié)點(diǎn),這是雙向隊(duì)列的操作,還有單向隊(duì)列询微,有興趣的可以去了解一下)撑毛。

看這一個(gè)方法里就出現(xiàn)了compareAndSetHead和compareAndSetTail兩個(gè)CAS操作,點(diǎn)進(jìn)去發(fā)現(xiàn)還是Unsafe實(shí)現(xiàn)的,由此可見(jiàn)這個(gè)類(lèi)在多線(xiàn)程中還是很重要的。

AQS中還有很多方法居暖,咱們不一一介紹了嘁圈,下面介紹幾個(gè)和資源共享相關(guān)的方法钞澳。

資源共享方式

AQS定義了兩種資源共享方式

  • 獨(dú)占資源,只有一個(gè)線(xiàn)程能執(zhí)行轧拄,如ReentrantLock
  • 共享資源拄丰,多個(gè)線(xiàn)程可以同時(shí)執(zhí)行料按,如Semaphore/CountDownLatch烹卒。

獨(dú)占模式采用tryAcquire-tryRelease實(shí)現(xiàn),共享模式采用tryAcquireShared-tryReleaseShared實(shí)現(xiàn)溺拱。有一些特殊的鎖兩種模式都使用迫摔,比如ReentrantReadWriteLock(讀寫(xiě)鎖)攒菠。

  • isHeldExclusively():該線(xiàn)程是否獨(dú)占資源。

    protected boolean isHeldExclusively() {
            throw new UnsupportedOperationException();
        }
    
  • tryAcquire(int):獨(dú)占方式凹炸。嘗試獲取資源昼弟。

    protected boolean tryAcquire(int arg) {
            throw new UnsupportedOperationException();
        }
    
  • tryRelease(int):獨(dú)占方式塌碌。嘗試釋放資源。

    protected boolean tryRelease(int arg) {
            throw new UnsupportedOperationException();
        }
    
  • tryAcquireShared(int):共享方式接剩。嘗試獲取資源遗座。負(fù)數(shù)表示失斣逼肌; 0 表示成功日麸,但沒(méi)有剩余
    可用資源;正數(shù)表示成功涕刚,且有剩余資源。

    protected int tryAcquireShared(int arg) {
            throw new UnsupportedOperationException();
        }
    
  • tryReleaseShared(int):共享方式晨缴。嘗試釋放資源睛竣,如果釋放后允許喚醒后續(xù)等待結(jié)點(diǎn)返回
    true验夯,否則返回 false。

    protected boolean tryReleaseShared(int arg) {
            throw new UnsupportedOperationException();
        }
    
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末豁护,一起剝皮案震驚了整個(gè)濱河市吝梅,隨后出現(xiàn)的幾起案子牍帚,更是在濱河造成了極大的恐慌岳锁,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異壤短,居然都是意外死亡万皿,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)浇借,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)阳距,“玉大人,你說(shuō)我怎么就攤上這事柳畔♀晒埽” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵薪韩,是天一觀(guān)的道長(zhǎng)确沸。 經(jīng)常有香客問(wèn)我,道長(zhǎng)俘陷,這世上最難降的妖魔是什么罗捎? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮拉盾,結(jié)果婚禮上桨菜,老公的妹妹穿的比我還像新娘。我一直安慰自己捉偏,他們只是感情好倒得,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著夭禽,像睡著了一般霞掺。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上讹躯,一...
    開(kāi)封第一講書(shū)人閱讀 51,692評(píng)論 1 305
  • 那天菩彬,我揣著相機(jī)與錄音,去河邊找鬼蜀撑。 笑死挤巡,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的酷麦。 我是一名探鬼主播矿卑,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼沃饶!你這毒婦竟也來(lái)了母廷?” 一聲冷哼從身側(cè)響起轻黑,我...
    開(kāi)封第一講書(shū)人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎琴昆,沒(méi)想到半個(gè)月后氓鄙,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡业舍,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年抖拦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片舷暮。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡态罪,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出下面,到底是詐尸還是另有隱情复颈,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布沥割,位于F島的核電站耗啦,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏机杜。R本人自食惡果不足惜帜讲,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望叉庐。 院中可真熱鬧舒帮,春花似錦、人聲如沸陡叠。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)枉阵。三九已至,卻和暖如春预茄,著一層夾襖步出監(jiān)牢的瞬間兴溜,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工耻陕, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拙徽,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓诗宣,卻偏偏與公主長(zhǎng)得像膘怕,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子召庞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

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