Java并發(fā)編程之AQS源碼閱讀

1. 父類AbstractOwnableSynchronizer

該類有兩個重要的方法,設(shè)置獨占線程與獲取獨占線程讲岁。

public abstract class AbstractOwnableSynchronizer
    implements java.io.Serializable {

    /** Use serial ID even though all fields transient. */
    private static final long serialVersionUID = 3737899427754241961L;

    /**
     * 空構(gòu)造方法
     */
    protected AbstractOwnableSynchronizer() { }

    /**
     * 同步器獨占模式當前線程
     */
    private transient Thread exclusiveOwnerThread;

    /**
     * 設(shè)置當前獨占線程
     */
    protected final void setExclusiveOwnerThread(Thread thread) {
        exclusiveOwnerThread = thread;
    }

    /**
     * 獲取當前獨占線程
     */
    protected final Thread getExclusiveOwnerThread() {
        return exclusiveOwnerThread;
    }
}

2. Node節(jié)點

用Node作為同步隊列和條件隊列的節(jié)點缓艳。

    static final class Node {
        /** 共享模式中的節(jié)點 */
        static final Node SHARED = new Node();
        /** 獨占模式中的節(jié)點 */
        static final Node EXCLUSIVE = null;

        /** 當線程等待超時或者被中斷阶淘,則取消等待溪窒,設(shè)等待狀態(tài)為-1澈蚌,進入取消狀態(tài)則不再變化 */
        static final int CANCELLED =  1;
        /** 后繼節(jié)點處于等待狀態(tài)灼狰,當前節(jié)點(為-1)被取消或者中斷時會通知后繼節(jié)點伏嗜,使后繼節(jié)點的線程得以運行 */
        static final int SIGNAL    = -1;
        /** 當前節(jié)點處于等待隊列伐厌,節(jié)點線程等待在Condition上挣轨,當其他線程對condition執(zhí)行signall方法時卷扮,等待隊列轉(zhuǎn)移到同步隊列晤锹,加入到對同步狀態(tài)的獲取 */
        static final int CONDITION = -2;
        /**
         * 值為-3鞭铆,與共享模式相關(guān)车遂,在共享模式中斯辰,該狀態(tài)標識結(jié)點的線程處于可運行狀態(tài)
         */
        static final int PROPAGATE = -3;

        /**
         * 等待狀態(tài)
         */
        volatile int waitStatus;

        /**
         * 指向前節(jié)點
         */
        volatile Node prev;

        /**
         * 指向后節(jié)點
         */
        volatile Node next;

        /**
         * 進入該節(jié)點的線程
         */
        volatile Thread thread;

        /**
         * 指向條件隊列中的下一個節(jié)點
         */
        Node nextWaiter;

        /**
         * 當節(jié)點在共享模式中等待時返回true
         */
        final boolean isShared() {
            return nextWaiter == SHARED;
        }

        /**
         * 返回前一節(jié)點
         */
        final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }

        Node() {    // Used to establish initial head or SHARED marker
        }

        Node(Thread thread, Node mode) {     // 被addWaiter方法調(diào)用
            this.nextWaiter = mode;
            this.thread = thread;
        }

        Node(Thread thread, int waitStatus) { // 被條件隊列使用
            this.waitStatus = waitStatus;
            this.thread = thread;
        }
    }

3. 成員變量

包含三個成員變量衣陶,head祖搓、tail以及state

    /**
     * 等待隊列的頭結(jié)點,如果頭結(jié)點存在的話拯欧,它的waitStatus不能是CANCELLED
     */
    private transient volatile Node head;

    /**
     * 等待隊列的尾節(jié)點
     */
    private transient volatile Node tail;

    /**
     * 同步器狀態(tài)
     */
    private volatile int state;

4. 主要方法

4.1 同步器狀態(tài)相關(guān)

    /**
     * 獲取當前同步器狀態(tài)
     */
    protected final int getState() {
        return state;
    }

    /**
     * 設(shè)置當前同步器狀態(tài)
     */
    protected final void setState(int newState) {
        state = newState;
    }

    /**
     * 調(diào)用魔法類的CAS方法,設(shè)置同步器狀態(tài)
     */
    protected final boolean compareAndSetState(int expect, int update) {
        // See below for intrinsics setup to support this
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }

4.2 acquire(int arg)方法

先嘗試獲取同步狀態(tài)该贾,如果獲取成功(同步狀態(tài)為0捌臊,表示可以獲取),直接返回曙寡;否則該線程加到等待隊列中進行排隊举庶。

    /**
     * 以獨占模式獲取户侥,忽略中斷蕊唐。 通過調(diào)用至少一次tryAcquire(int)實現(xiàn)烁设,成功返回署尤。
     * 否則線程排隊曹体,可能會重復(fù)阻塞和解除阻塞硝烂,直到成功才調(diào)用tryAcquire(int) 。 該方法可用于實現(xiàn)方法Lock.lock() 串稀。
     */
    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

4.3 tryAcquire(int arg)方法

交給子類實現(xiàn)

4.4 addWaiter(Node mode)方法

將新節(jié)點加到尾部

    /**
     * 在尾部添加節(jié)點
     */
    private Node addWaiter(Node mode) {
        // 構(gòu)建一個新node母截,nextWaiter是當前node
        Node node = new Node(Thread.currentThread(), mode);
        // 獲取尾結(jié)點
        Node pred = tail;
        // 如果尾節(jié)點不為空清寇,將當前節(jié)點作為尾結(jié)點
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        //尾節(jié)點為空华烟,將當前節(jié)點入隊
        enq(node);
        return node;
    }

4.5 enq(final Node node)方法

將當前節(jié)點入隊盔夜。

    private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { //尾結(jié)點為空返十,必須初始化
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

4.6 acquireQueued(final Node node, int arg)方法

不斷自旋嘗試獲取同步狀態(tài)(鎖)吧慢,獲取不成功检诗,則阻塞節(jié)點中的線程逢慌。

final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            //自旋獲取同步狀態(tài)
            for (;;) {
                //獲取前一個節(jié)點
                final Node p = node.predecessor();
                //如果前一個節(jié)點是頭結(jié)點并且獲取同步狀態(tài)成功攻泼,就將當前節(jié)點置為頭結(jié)點
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())    // 休眠線程并返回中斷狀態(tài)
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

4.7 release(int arg)方法

在獨占模式中釋放節(jié)點。當返回true的時候牛欢,unblocking一個或多個線程傍睹。

    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

4.8 tryRelease(int arg)方法

嘗試釋放同步狀態(tài)拾稳,留給子類實現(xiàn)

    protected boolean tryRelease(int arg) {
        throw new UnsupportedOperationException();
    }

4.9 unparkSuccessor(Node node)方法

喚醒線程

private void unparkSuccessor(Node node) {
        // 獲取wait狀態(tài)
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        /**
         * 若后繼結(jié)點為空访得,或狀態(tài)為CANCEL(已失效)陕凹,則從后尾部往前遍歷找到最前的一個處于正常阻塞狀態(tài)的結(jié)點
         * 進行喚醒
         */
        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);//喚醒線程
    }

4.10 acquireShared(int arg)方法

在共享模式中獲得鎖传趾。

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

4.11 tryAcquireShared(int arg)

嘗試獲取鎖磕仅,當返回值大于等于0時榕订,表示能夠獲取到同步狀態(tài)蜕便,空方法留給子類實現(xiàn)劫恒。

4.12 doAcquireShared(int arg)方法

如果當前節(jié)點的前驅(qū)為頭節(jié)點時,嘗試獲取同步狀態(tài)

private void doAcquireShared(int arg) {
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        if (interrupted)
                            selfInterrupt();
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

4.13 releaseShared(int arg)方法

該方法在釋放同步狀態(tài)之后轿腺,將會喚醒后續(xù)處于等待狀態(tài)的節(jié)點两嘴。對于能夠支持多個線 程同時訪問的并發(fā)組件(比如Semaphore),它和獨占式主要區(qū)別在于tryReleaseShared(int arg) 方法必須確保同步狀態(tài)(或者資源數(shù))線程安全釋放族壳,一般是通過循環(huán)和CAS來保證的憔辫,因為 釋放同步狀態(tài)的操作會同時來自多個線程。

    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末仿荆,一起剝皮案震驚了整個濱河市贰您,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌拢操,老刑警劉巖锦亦,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件玲昧,死亡現(xiàn)場離奇詭異吕漂,居然都是意外死亡苍鲜,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進店門领跛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來矢棚,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵俩块,是天一觀的道長联贩。 經(jīng)常有香客問我盲厌,道長,這世上最難降的妖魔是什么微王? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任啊央,我火速辦了婚禮乓土,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘萄金。我一直安慰自己,他們只是感情好份氧,可當我...
    茶點故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布湘捎。 她就那樣靜靜地躺著活翩,像睡著了一般脸爱。 火紅的嫁衣襯著肌膚如雪簿废。 梳的紋絲不亂的頭發(fā)上单料,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天掠廓,我揣著相機與錄音,去河邊找鬼彻坛。 笑死,一個胖子當著我的面吹牛抢呆,可吹牛的內(nèi)容都是我干的谣沸。 我是一名探鬼主播刷钢,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼乳附!你這毒婦竟也來了内地?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤赋除,失蹤者是張志新(化名)和其女友劉穎阱缓,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贤重,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡茬祷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了并蝗。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片祭犯。...
    茶點故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖滚停,靈堂內(nèi)的尸體忽然破棺而出沃粗,到底是詐尸還是另有隱情,我是刑警寧澤键畴,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布最盅,位于F島的核電站,受9級特大地震影響起惕,放射性物質(zhì)發(fā)生泄漏涡贱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一惹想、第九天 我趴在偏房一處隱蔽的房頂上張望问词。 院中可真熱鬧,春花似錦嘀粱、人聲如沸激挪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽垄分。三九已至,卻和暖如春娃磺,著一層夾襖步出監(jiān)牢的瞬間薄湿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工偷卧, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留嘿般,地道東北人。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓涯冠,卻偏偏與公主長得像炉奴,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蛇更,可洞房花燭夜當晚...
    茶點故事閱讀 45,870評論 2 361

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