AQS --- 漸入佳境

上一講了解了 AQS 是什么唁影,接下來看看它到底是怎樣的結(jié)構(gòu)愉耙。

一. 工作原理

AQS 使用一個 volatile 的 int 類型的成員變量來表示同步狀態(tài)钞支,通過內(nèi)置的 FIFO 隊列來完成資源獲取和排隊工作,將每條要去搶占資源的線程封裝成一個 node 節(jié)點來實現(xiàn)鎖的分配,通過 CAS 來完成對 state 值的修改骤竹。

HashMap 進行 put 的時候,也不是直接存儲 key value 鍵值對往毡,而是將 key value 鍵值對封裝成 Node 節(jié)點瘤载,然后用數(shù)組 + 鏈表 + 紅黑樹存儲 Node。AQS 也類似卖擅,將要搶占資源的 Thread 封裝成 Node節(jié)點鸣奔。

二. 相關(guān)源碼:

public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
   
    static final class Node {
       ……
       volatile int waitStatus;
       volatile Node prev;
       volatile Node next;
       volatile Thread thread;
       ……
    }

    private transient volatile Node head;
    private transient volatile Node tail;

    /**
     * The synchronization state.
     */
    private volatile int state;
}

看到這個是不是就清清楚楚明明白白真真切切了。首先 AQS 外層是 state + CLH 隊列惩阶,state 表示同步的狀態(tài)挎狸,默認是0,為0時表示可以獲取鎖断楷,不為0時锨匆,線程就得老老實實到隊列中排隊去;CLH 隊列就是一個有頭結(jié)點和尾結(jié)點的雙端隊列冬筒,如下圖:

           +------+  prev +-----+       +-----+
      head |      | <---- |     | <---- |     |  tail
           +------+       +-----+       +-----+

AQS 的內(nèi)層是一個 Node內(nèi)部類恐锣,這個 Node 類主要有兩個指針 prev 和 next、一個 waitStatus 表示線程的狀舞痰、土榴,一個 Thread 類型的變量保存等待的線程。

三. 從 ReentrantLock 看 AQS:

之前說了 AQS 是 JUC 并發(fā)包的基石响牛,那就從我們接觸最多的 ReentrantLock 入手玷禽,揭開它的神秘面紗。

先來看看 ReentrantLock 的結(jié)構(gòu)圖:

結(jié)構(gòu)圖

首先它實現(xiàn)了 Lock 接口呀打,其內(nèi)部主要是一個 Sync 內(nèi)部類矢赁,這個內(nèi)部類又有兩個子類,一個 FairSync 和一個 NonfairSync贬丛,分別用來實現(xiàn)公平鎖和非公平鎖撩银。而這個 Sync 內(nèi)部類,又是 AbstractQueuedSynchronizer 的子類豺憔。

1. 我們 new ReentrantLock 的時候做了什么事额获?

/**
 * Creates an instance of {@code ReentrantLock}.
 * This is equivalent to using {@code ReentrantLock(false)}.
 */
 public ReentrantLock() {
     sync = new NonfairSync();
 }

通過這個構(gòu)造方法可以知道,實際上是構(gòu)建了一個非公平鎖焕阿。如果 new 的時候傳了 true咪啡,調(diào)用的構(gòu)造方法就是:

/**
 * Creates an instance of {@code ReentrantLock} with the
 * given fairness policy.
 *
 * @param fair {@code true} if this lock should use a fair ordering policy
 */
 public ReentrantLock(boolean fair) {
     sync = fair ? new FairSync() : new NonfairSync();
 }

所以傳的是 true,構(gòu)建的就是公平鎖暮屡。

2. 公平和非公平有什么區(qū)別?

非公平鎖源碼:

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

公平鎖源碼:

 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;
}

乍一看兩段代碼好像沒啥不一樣毅桃,其實不同之處在褒纲,if (c == 0)這段判斷中准夷。公平鎖多了一個判斷條件,即!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; // Read fields in reverse initialization order
    Node h = head;
    Node s;
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}

這個方法也很簡單衫嵌,首先是頭節(jié)點不等于尾節(jié)點,然后就是頭節(jié)點的下一個節(jié)點為空或者頭節(jié)點的下一個節(jié)點保存的 Thread 不等于當(dāng)前的 Thread彻秆。簡單地說就是看隊列中有沒有除了當(dāng)前 Thread 以為的 Thread 在等待獲取鎖楔绞,有就返回 true,否則返回 false唇兑。所以公平鎖就是多了這個判斷酒朵,其他都一樣。

下一篇文章將會從源碼層面分析 ReentrantLock 的加鎖過程扎附,敬請期待蔫耽!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市留夜,隨后出現(xiàn)的幾起案子匙铡,更是在濱河造成了極大的恐慌,老刑警劉巖碍粥,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鳖眼,死亡現(xiàn)場離奇詭異,居然都是意外死亡嚼摩,警方通過查閱死者的電腦和手機具帮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來低斋,“玉大人蜂厅,你說我怎么就攤上這事〔渤耄” “怎么了掘猿?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長唇跨。 經(jīng)常有香客問我稠通,道長,這世上最難降的妖魔是什么买猖? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任改橘,我火速辦了婚禮,結(jié)果婚禮上玉控,老公的妹妹穿的比我還像新娘飞主。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布碌识。 她就那樣靜靜地躺著碾篡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪筏餐。 梳的紋絲不亂的頭發(fā)上开泽,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天,我揣著相機與錄音魁瞪,去河邊找鬼穆律。 笑死,一個胖子當(dāng)著我的面吹牛导俘,可吹牛的內(nèi)容都是我干的峦耘。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼趟畏,長吁一口氣:“原來是場噩夢啊……” “哼贡歧!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起赋秀,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤利朵,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后猎莲,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體绍弟,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年著洼,在試婚紗的時候發(fā)現(xiàn)自己被綠了樟遣。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡身笤,死狀恐怖豹悬,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情液荸,我是刑警寧澤瞻佛,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站娇钱,受9級特大地震影響伤柄,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜文搂,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一适刀、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧煤蹭,春花似錦笔喉、人聲如沸取视。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽贫途。三九已至吧彪,卻和暖如春待侵,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背姨裸。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工秧倾, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人傀缩。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓那先,卻偏偏與公主長得像,于是被迫代替她去往敵國和親赡艰。 傳聞我的和親對象是個殘疾皇子售淡,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,486評論 2 348

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