欧美一区二区三区老妇人-欧美做爰猛烈大尺度电-99久久夜色精品国产亚洲a-亚洲福利视频一区二区

JavaJUC的核心類(lèi)AQS有什么用

這篇文章主要介紹“Java JUC的核心類(lèi)AQS有什么用”,在日常操作中,相信很多人在Java JUC的核心類(lèi)AQS有什么用問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”Java JUC的核心類(lèi)AQS有什么用”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!

10年的寶山網(wǎng)站建設(shè)經(jīng)驗(yàn),針對(duì)設(shè)計(jì)、前端、開(kāi)發(fā)、售后、文案、推廣等六對(duì)一服務(wù),響應(yīng)快,48小時(shí)及時(shí)工作處理。營(yíng)銷(xiāo)型網(wǎng)站的優(yōu)勢(shì)是能夠根據(jù)用戶(hù)設(shè)備顯示端的尺寸不同,自動(dòng)調(diào)整寶山建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無(wú)論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計(jì),從而大程度地提升瀏覽體驗(yàn)。創(chuàng)新互聯(lián)公司從事“寶山網(wǎng)站設(shè)計(jì)”,“寶山網(wǎng)站推廣”以來(lái),每個(gè)客戶(hù)項(xiàng)目都認(rèn)真落實(shí)執(zhí)行。

簡(jiǎn)介

在分析 Java 并發(fā)包 java.util.concurrent 源碼的時(shí)候,少不了需要了解 AbstractQueuedSynchronizer(以下簡(jiǎn)寫(xiě)AQS)這個(gè)抽象類(lèi),因?yàn)樗?Java 并發(fā)包的基礎(chǔ)工具類(lèi),是實(shí)現(xiàn) ReentrantLock、CountDownLatch、Semaphore、FutureTask 等類(lèi)的基礎(chǔ)。

Google 一下 AbstractQueuedSynchronizer,我們可以找到很多關(guān)于 AQS 的介紹,但是很多都沒(méi)有介紹清楚,因?yàn)榇蟛糠治恼聸](méi)有把其中的一些關(guān)鍵的細(xì)節(jié)說(shuō)清楚。

本文將從 ReentrantLock 的公平鎖源碼出發(fā),分析下 AbstractQueuedSynchronizer 這個(gè)類(lèi)是怎么工作的,希望能給大家提供一些簡(jiǎn)單的幫助。

申明以下幾點(diǎn):

  1. 本文有點(diǎn)長(zhǎng),但還是挺簡(jiǎn)單,主要面向讀者對(duì)象為并發(fā)編程的初學(xué)者,或者想要閱讀 Java 并發(fā)包源碼的開(kāi)發(fā)者。對(duì)于新手來(lái)說(shuō),可能需要花好幾個(gè)小時(shí)才能完全看懂,但是這時(shí)間肯定是值得的。

  2. 源碼環(huán)境 JDK1.7(1.8沒(méi)啥變化),看到不懂或有疑惑的部分,最好能自己打開(kāi)源碼看看。Doug Lea 大神的代碼寫(xiě)得真心不錯(cuò)。

  3. 本文不分析共享模式,這樣可以給讀者減少很多負(fù)擔(dān), 第三篇文章對(duì)共享模式進(jìn)行了分析。而且也不分析 condition 部分,所以應(yīng)該說(shuō)很容易就可以看懂了。

  4. 本文大量使用我們平時(shí)用得最多的 ReentrantLock 的概念,本質(zhì)上來(lái)說(shuō)是不正確的,讀者應(yīng)該清楚,AQS 不僅僅用來(lái)實(shí)現(xiàn)可重入鎖,只是希望讀者可以用鎖來(lái)聯(lián)想 AQS 的使用場(chǎng)景,降低閱讀壓力。

  5. ReentrantLock 的公平鎖和非公平鎖只有一點(diǎn)點(diǎn)區(qū)別, 第二篇文章做了介紹。

  6. 評(píng)論區(qū)有讀者反饋本文直接用代碼說(shuō)不友好,應(yīng)該多配點(diǎn)流程圖,這篇文章確實(shí)有這個(gè)問(wèn)題。但是作為過(guò)來(lái)人,我想告訴大家,對(duì)于 AQS 來(lái)說(shuō),形式真的不重要,重要的是把細(xì)節(jié)說(shuō)清楚。

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

先來(lái)看看 AQS 有哪些屬性,搞清楚這些基本就知道 AQS 是什么套路了,畢竟可以猜嘛!

// 頭結(jié)點(diǎn),你直接把它當(dāng)做 當(dāng)前持有鎖的線(xiàn)程 可能是最好理解的
private transient volatile Node head;
// 阻塞的尾節(jié)點(diǎn),每個(gè)新的節(jié)點(diǎn)進(jìn)來(lái),都插入到最后,也就形成了一個(gè)鏈表
private transient volatile Node tail;
// 這個(gè)是最重要的,代表當(dāng)前鎖的狀態(tài),0代表沒(méi)有被占用,大于 0 代表有線(xiàn)程持有當(dāng)前鎖
// 這個(gè)值可以大于 1,是因?yàn)殒i可以重入,每次重入都加上 1
private volatile int state;
// 代表當(dāng)前持有獨(dú)占鎖的線(xiàn)程,舉個(gè)最重要的使用例子,因?yàn)殒i可以重入
// reentrantLock.lock()可以嵌套調(diào)用多次,所以每次用這個(gè)來(lái)判斷當(dāng)前線(xiàn)程是否已經(jīng)擁有了鎖
// if (currentThread == getExclusiveOwnerThread()) {state++}
private transient Thread exclusiveOwnerThread; //繼承自AbstractOwnableSynchronizer

怎么樣,看樣子應(yīng)該是很簡(jiǎn)單的吧,畢竟也就四個(gè)屬性啊。

AbstractQueuedSynchronizer 的等待隊(duì)列示意如下所示,注意了,之后分析過(guò)程中所說(shuō)的 queue,也就是阻塞隊(duì)列不包含 head,不包含 head,不包含 head

Java JUC的核心類(lèi)AQS有什么用

等待隊(duì)列中每個(gè)線(xiàn)程被包裝成一個(gè) Node 實(shí)例,數(shù)據(jù)結(jié)構(gòu)是鏈表,一起看看源碼吧:

static final class Node {
    // 標(biāo)識(shí)節(jié)點(diǎn)當(dāng)前在共享模式下
    static final Node SHARED = new Node();
    // 標(biāo)識(shí)節(jié)點(diǎn)當(dāng)前在獨(dú)占模式下
    static final Node EXCLUSIVE = null;
    // ======== 下面的幾個(gè)int常量是給waitStatus用的 ===========
    /** waitStatus value to indicate thread has cancelled */
    // 代碼此線(xiàn)程取消了爭(zhēng)搶這個(gè)鎖
    static final int CANCELLED =  1;
    /** waitStatus value to indicate successor's thread needs unparking */
    // 官方的描述是,其表示當(dāng)前node的后繼節(jié)點(diǎn)對(duì)應(yīng)的線(xiàn)程需要被喚醒
    static final int SIGNAL    = -1;
    /** waitStatus value to indicate thread is waiting on condition */
    // 本文不分析condition,所以略過(guò)吧,下一篇文章會(huì)介紹這個(gè)
    static final int CONDITION = -2;
    /**
     * waitStatus value to indicate the next acquireShared should
     * unconditionally propagate
     */
    // 同樣的不分析,略過(guò)吧
    static final int PROPAGATE = -3;
    // =====================================================
    // 取值為上面的1、-1、-2、-3,或者0(以后會(huì)講到)
    // 這么理解,暫時(shí)只需要知道如果這個(gè)值 大于0 代表此線(xiàn)程取消了等待,
    //    ps: 半天搶不到鎖,不搶了,ReentrantLock是可以指定timeouot的。。。
    volatile int waitStatus;
    // 前驅(qū)節(jié)點(diǎn)的引用
    volatile Node prev;
    // 后繼節(jié)點(diǎn)的引用
    volatile Node next;
    // 這個(gè)就是線(xiàn)程本尊
    volatile Thread thread;
}

Node 的數(shù)據(jù)結(jié)構(gòu)其實(shí)也挺簡(jiǎn)單的,就是 thread + waitStatus + pre + next 四個(gè)屬性而已,大家先要有這個(gè)概念在心里。

上面的是基礎(chǔ)知識(shí),后面會(huì)多次用到,心里要時(shí)刻記著它們,心里想著這個(gè)結(jié)構(gòu)圖就可以了。下面,我們開(kāi)始說(shuō) ReentrantLock 的公平鎖。再次強(qiáng)調(diào),我說(shuō)的阻塞隊(duì)列不包含 head 節(jié)點(diǎn)。

Java JUC的核心類(lèi)AQS有什么用

首先,我們先看下 ReentrantLock 的使用方式。

// 我用個(gè)web開(kāi)發(fā)中的service概念吧
public class OrderService {
    // 使用static,這樣每個(gè)線(xiàn)程拿到的是同一把鎖,當(dāng)然,spring mvc中service默認(rèn)就是單例,別糾結(jié)這個(gè)
    private static ReentrantLock reentrantLock = new ReentrantLock(true);
    public void createOrder() {
        // 比如我們同一時(shí)間,只允許一個(gè)線(xiàn)程創(chuàng)建訂單
        reentrantLock.lock();
        // 通常,lock 之后緊跟著 try 語(yǔ)句
        try {
            // 這塊代碼同一時(shí)間只能有一個(gè)線(xiàn)程進(jìn)來(lái)(獲取到鎖的線(xiàn)程),
            // 其他的線(xiàn)程在lock()方法上阻塞,等待獲取到鎖,再進(jìn)來(lái)
            // 執(zhí)行代碼...
            // 執(zhí)行代碼...
            // 執(zhí)行代碼...
        } finally {
            // 釋放鎖
            reentrantLock.unlock();
        }
    }
}

ReentrantLock 在內(nèi)部用了內(nèi)部類(lèi) Sync 來(lái)管理鎖,所以真正的獲取鎖和釋放鎖是由 Sync 的實(shí)現(xiàn)類(lèi)來(lái)控制的。

abstract static class Sync extends AbstractQueuedSynchronizer {
}

Sync 有兩個(gè)實(shí)現(xiàn),分別為 NonfairSync(非公平鎖)和 FairSync(公平鎖),我們看 FairSync 部分。

public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

線(xiàn)程搶鎖

很多人肯定開(kāi)始嫌棄上面廢話(huà)太多了,下面跟著代碼走,我就不廢話(huà)了。

static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;
      // 爭(zhēng)鎖
    final void lock() {
        acquire(1);
    }
      // 來(lái)自父類(lèi)AQS,我直接貼過(guò)來(lái)這邊,下面分析的時(shí)候同樣會(huì)這樣做,不會(huì)給讀者帶來(lái)閱讀壓力
    // 我們看到,這個(gè)方法,如果tryAcquire(arg) 返回true, 也就結(jié)束了。
    // 否則,acquireQueued方法會(huì)將線(xiàn)程壓到隊(duì)列中
    public final void acquire(int arg) { // 此時(shí) arg == 1
        // 首先調(diào)用tryAcquire(1)一下,名字上就知道,這個(gè)只是試一試
        // 因?yàn)橛锌赡苤苯泳统晒α四兀簿筒恍枰M(jìn)隊(duì)列排隊(duì)了,
        // 對(duì)于公平鎖的語(yǔ)義就是:本來(lái)就沒(méi)人持有鎖,根本沒(méi)必要進(jìn)隊(duì)列等待(又是掛起,又是等待被喚醒的)
        if (!tryAcquire(arg) &&
            // tryAcquire(arg)沒(méi)有成功,這個(gè)時(shí)候需要把當(dāng)前線(xiàn)程掛起,放到阻塞隊(duì)列中。
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) {
              selfInterrupt();
        }
    }
    /**
     * Fair version of tryAcquire.  Don't grant access unless
     * recursive call or no waiters or is first.
     */
    // 嘗試直接獲取鎖,返回值是boolean,代表是否獲取到鎖
    // 返回true:1.沒(méi)有線(xiàn)程在等待鎖;2.重入鎖,線(xiàn)程本來(lái)就持有鎖,也就可以理所當(dāng)然可以直接獲取
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        // state == 0 此時(shí)此刻沒(méi)有線(xiàn)程持有鎖
        if (c == 0) {
            // 雖然此時(shí)此刻鎖是可以用的,但是這是公平鎖,既然是公平,就得講究先來(lái)后到,
            // 看看有沒(méi)有別人在隊(duì)列中等了半天了
            if (!hasQueuedPredecessors() &&
                // 如果沒(méi)有線(xiàn)程在等待,那就用CAS嘗試一下,成功了就獲取到鎖了,
                // 不成功的話(huà),只能說(shuō)明一個(gè)問(wèn)題,就在剛剛幾乎同一時(shí)刻有個(gè)線(xiàn)程搶先了 =_=
                // 因?yàn)閯倓傔€沒(méi)人的,我判斷過(guò)了
                compareAndSetState(0, acquires)) {
                // 到這里就是獲取到鎖了,標(biāo)記一下,告訴大家,現(xiàn)在是我占用了鎖
                setExclusiveOwnerThread(current);
                return true;
            }
        }
          // 會(huì)進(jìn)入這個(gè)else if分支,說(shuō)明是重入了,需要操作:state=state+1
        // 這里不存在并發(fā)問(wèn)題
        else if (current == getExclusiveOwnerThread()) {
            int nextc = c + acquires;
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        // 如果到這里,說(shuō)明前面的if和else if都沒(méi)有返回true,說(shuō)明沒(méi)有獲取到鎖
        // 回到上面一個(gè)外層調(diào)用方法繼續(xù)看:
        // if (!tryAcquire(arg) 
        //        && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) 
        //     selfInterrupt();
        return false;
    }
    // 假設(shè)tryAcquire(arg) 返回false,那么代碼將執(zhí)行:
      //        acquireQueued(addWaiter(Node.EXCLUSIVE), arg),
    // 這個(gè)方法,首先需要執(zhí)行:addWaiter(Node.EXCLUSIVE)
    /**
     * Creates and enqueues node for current thread and given mode.
     *
     * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
     * @return the new node
     */
    // 此方法的作用是把線(xiàn)程包裝成node,同時(shí)進(jìn)入到隊(duì)列中
    // 參數(shù)mode此時(shí)是Node.EXCLUSIVE,代表獨(dú)占模式
    private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        // 以下幾行代碼想把當(dāng)前node加到鏈表的最后面去,也就是進(jìn)到阻塞隊(duì)列的最后
        Node pred = tail;
        // tail!=null => 隊(duì)列不為空(tail==head的時(shí)候,其實(shí)隊(duì)列是空的,不過(guò)不管這個(gè)吧)
        if (pred != null) { 
            // 將當(dāng)前的隊(duì)尾節(jié)點(diǎn),設(shè)置為自己的前驅(qū) 
            node.prev = pred; 
            // 用CAS把自己設(shè)置為隊(duì)尾, 如果成功后,tail == node 了,這個(gè)節(jié)點(diǎn)成為阻塞隊(duì)列新的尾巴
            if (compareAndSetTail(pred, node)) { 
                // 進(jìn)到這里說(shuō)明設(shè)置成功,當(dāng)前node==tail, 將自己與之前的隊(duì)尾相連,
                // 上面已經(jīng)有 node.prev = pred,加上下面這句,也就實(shí)現(xiàn)了和之前的尾節(jié)點(diǎn)雙向連接了
                pred.next = node;
                // 線(xiàn)程入隊(duì)了,可以返回了
                return node;
            }
        }
        // 仔細(xì)看看上面的代碼,如果會(huì)到這里,
        // 說(shuō)明 pred==null(隊(duì)列是空的) 或者 CAS失敗(有線(xiàn)程在競(jìng)爭(zhēng)入隊(duì))
        // 讀者一定要跟上思路,如果沒(méi)有跟上,建議先不要往下讀了,往回仔細(xì)看,否則會(huì)浪費(fèi)時(shí)間的
        enq(node);
        return node;
    }
    /**
     * Inserts node into queue, initializing if necessary. See picture above.
     * @param node the node to insert
     * @return node's predecessor
     */
    // 采用自旋的方式入隊(duì)
    // 之前說(shuō)過(guò),到這個(gè)方法只有兩種可能:等待隊(duì)列為空,或者有線(xiàn)程競(jìng)爭(zhēng)入隊(duì),
    // 自旋在這邊的語(yǔ)義是:CAS設(shè)置tail過(guò)程中,競(jìng)爭(zhēng)一次競(jìng)爭(zhēng)不到,我就多次競(jìng)爭(zhēng),總會(huì)排到的
    private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            // 之前說(shuō)過(guò),隊(duì)列為空也會(huì)進(jìn)來(lái)這里
            if (t == null) { // Must initialize
                // 初始化head節(jié)點(diǎn)
                // 細(xì)心的讀者會(huì)知道原來(lái) head 和 tail 初始化的時(shí)候都是 null 的
                // 還是一步CAS,你懂的,現(xiàn)在可能是很多線(xiàn)程同時(shí)進(jìn)來(lái)呢
                if (compareAndSetHead(new Node()))
                    // 給后面用:這個(gè)時(shí)候head節(jié)點(diǎn)的waitStatus==0, 看new Node()構(gòu)造方法就知道了
                    // 這個(gè)時(shí)候有了head,但是tail還是null,設(shè)置一下,
                    // 把tail指向head,放心,馬上就有線(xiàn)程要來(lái)了,到時(shí)候tail就要被搶了
                    // 注意:這里只是設(shè)置了tail=head,這里可沒(méi)return哦,沒(méi)有return,沒(méi)有return
                    // 所以,設(shè)置完了以后,繼續(xù)for循環(huán),下次就到下面的else分支了
                    tail = head;
            } else {
                // 下面幾行,和上一個(gè)方法 addWaiter 是一樣的,
                // 只是這個(gè)套在無(wú)限循環(huán)里,反正就是將當(dāng)前線(xiàn)程排到隊(duì)尾,有線(xiàn)程競(jìng)爭(zhēng)的話(huà)排不上重復(fù)排
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }
    // 現(xiàn)在,又回到這段代碼了
    // if (!tryAcquire(arg) 
    //        && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) 
    //     selfInterrupt();
    // 下面這個(gè)方法,參數(shù)node,經(jīng)過(guò)addWaiter(Node.EXCLUSIVE),此時(shí)已經(jīng)進(jìn)入阻塞隊(duì)列
    // 注意一下:如果acquireQueued(addWaiter(Node.EXCLUSIVE), arg))返回true的話(huà),
    // 意味著上面這段代碼將進(jìn)入selfInterrupt(),所以正常情況下,下面應(yīng)該返回false
    // 這個(gè)方法非常重要,應(yīng)該說(shuō)真正的線(xiàn)程掛起,然后被喚醒后去獲取鎖,都在這個(gè)方法里了
    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                // p == head 說(shuō)明當(dāng)前節(jié)點(diǎn)雖然進(jìn)到了阻塞隊(duì)列,但是是阻塞隊(duì)列的第一個(gè),因?yàn)樗那膀?qū)是head
                // 注意,阻塞隊(duì)列不包含head節(jié)點(diǎn),head一般指的是占有鎖的線(xiàn)程,head后面的才稱(chēng)為阻塞隊(duì)列
                // 所以當(dāng)前節(jié)點(diǎn)可以去試搶一下鎖
                // 這里我們說(shuō)一下,為什么可以去試試:
                // 首先,它是隊(duì)頭,這個(gè)是第一個(gè)條件,其次,當(dāng)前的head有可能是剛剛初始化的node,
                // enq(node) 方法里面有提到,head是延時(shí)初始化的,而且new Node()的時(shí)候沒(méi)有設(shè)置任何線(xiàn)程
                // 也就是說(shuō),當(dāng)前的head不屬于任何一個(gè)線(xiàn)程,所以作為隊(duì)頭,可以去試一試,
                // tryAcquire已經(jīng)分析過(guò)了, 忘記了請(qǐng)往前看一下,就是簡(jiǎn)單用CAS試操作一下state
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                // 到這里,說(shuō)明上面的if分支沒(méi)有成功,要么當(dāng)前node本來(lái)就不是隊(duì)頭,
                // 要么就是tryAcquire(arg)沒(méi)有搶贏別人,繼續(xù)往下看
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            // 什么時(shí)候 failed 會(huì)為 true???
            // tryAcquire() 方法拋異常的情況
            if (failed)
                cancelAcquire(node);
        }
    }
    /**
     * Checks and updates status for a node that failed to acquire.
     * Returns true if thread should block. This is the main signal
     * control in all acquire loops.  Requires that pred == node.prev
     *
     * @param pred node's predecessor holding status
     * @param node the node
     * @return {@code true} if thread should block
     */
    // 剛剛說(shuō)過(guò),會(huì)到這里就是沒(méi)有搶到鎖唄,這個(gè)方法說(shuō)的是:"當(dāng)前線(xiàn)程沒(méi)有搶到鎖,是否需要掛起當(dāng)前線(xiàn)程?"
    // 第一個(gè)參數(shù)是前驅(qū)節(jié)點(diǎn),第二個(gè)參數(shù)才是代表當(dāng)前線(xiàn)程的節(jié)點(diǎn)
    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        // 前驅(qū)節(jié)點(diǎn)的 waitStatus == -1 ,說(shuō)明前驅(qū)節(jié)點(diǎn)狀態(tài)正常,當(dāng)前線(xiàn)程需要掛起,直接可以返回true
        if (ws == Node.SIGNAL)
            /*
             * This node has already set status asking a release
             * to signal it, so it can safely park.
             */
            return true;
        // 前驅(qū)節(jié)點(diǎn) waitStatus大于0 ,之前說(shuō)過(guò),大于0 說(shuō)明前驅(qū)節(jié)點(diǎn)取消了排隊(duì)。
        // 這里需要知道這點(diǎn):進(jìn)入阻塞隊(duì)列排隊(duì)的線(xiàn)程會(huì)被掛起,而喚醒的操作是由前驅(qū)節(jié)點(diǎn)完成的。
        // 所以下面這塊代碼說(shuō)的是將當(dāng)前節(jié)點(diǎn)的prev指向waitStatus<=0的節(jié)點(diǎn),
        // 簡(jiǎn)單說(shuō),就是為了找個(gè)好爹,因?yàn)槟氵€得依賴(lài)它來(lái)喚醒呢,如果前驅(qū)節(jié)點(diǎn)取消了排隊(duì),
        // 找前驅(qū)節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)做爹,往前遍歷總能找到一個(gè)好爹的
        if (ws > 0) {
            /*
             * Predecessor was cancelled. Skip over predecessors and
             * indicate retry.
             */
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            /*
             * waitStatus must be 0 or PROPAGATE.  Indicate that we
             * need a signal, but don't park yet.  Caller will need to
             * retry to make sure it cannot acquire before parking.
             */
            // 仔細(xì)想想,如果進(jìn)入到這個(gè)分支意味著什么
            // 前驅(qū)節(jié)點(diǎn)的waitStatus不等于-1和1,那也就是只可能是0,-2,-3
            // 在我們前面的源碼中,都沒(méi)有看到有設(shè)置waitStatus的,所以每個(gè)新的node入隊(duì)時(shí),waitStatu都是0
            // 正常情況下,前驅(qū)節(jié)點(diǎn)是之前的 tail,那么它的 waitStatus 應(yīng)該是 0
            // 用CAS將前驅(qū)節(jié)點(diǎn)的waitStatus設(shè)置為Node.SIGNAL(也就是-1)
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        // 這個(gè)方法返回 false,那么會(huì)再走一次 for 循序,
        //     然后再次進(jìn)來(lái)此方法,此時(shí)會(huì)從第一個(gè)分支返回 true
        return false;
    }
    // private static boolean shouldParkAfterFailedAcquire(Node pred, Node node)
    // 這個(gè)方法結(jié)束根據(jù)返回值我們簡(jiǎn)單分析下:
    // 如果返回true, 說(shuō)明前驅(qū)節(jié)點(diǎn)的waitStatus==-1,是正常情況,那么當(dāng)前線(xiàn)程需要被掛起,等待以后被喚醒
    //        我們也說(shuō)過(guò),以后是被前驅(qū)節(jié)點(diǎn)喚醒,就等著前驅(qū)節(jié)點(diǎn)拿到鎖,然后釋放鎖的時(shí)候叫你好了
    // 如果返回false, 說(shuō)明當(dāng)前不需要被掛起,為什么呢?往后看
    // 跳回到前面是這個(gè)方法
    // if (shouldParkAfterFailedAcquire(p, node) &&
    //                parkAndCheckInterrupt())
    //                interrupted = true;
    // 1\. 如果shouldParkAfterFailedAcquire(p, node)返回true,
    // 那么需要執(zhí)行parkAndCheckInterrupt():
    // 這個(gè)方法很簡(jiǎn)單,因?yàn)榍懊娣祷豻rue,所以需要掛起線(xiàn)程,這個(gè)方法就是負(fù)責(zé)掛起線(xiàn)程的
    // 這里用了LockSupport.park(this)來(lái)掛起線(xiàn)程,然后就停在這里了,等待被喚醒=======
    private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }
    // 2\. 接下來(lái)說(shuō)說(shuō)如果shouldParkAfterFailedAcquire(p, node)返回false的情況
   // 仔細(xì)看shouldParkAfterFailedAcquire(p, node),我們可以發(fā)現(xiàn),其實(shí)第一次進(jìn)來(lái)的時(shí)候,一般都不會(huì)返回true的,原因很簡(jiǎn)單,前驅(qū)節(jié)點(diǎn)的waitStatus=-1是依賴(lài)于后繼節(jié)點(diǎn)設(shè)置的。也就是說(shuō),我都還沒(méi)給前驅(qū)設(shè)置-1呢,怎么可能是true呢,但是要看到,這個(gè)方法是套在循環(huán)里的,所以第二次進(jìn)來(lái)的時(shí)候狀態(tài)就是-1了。
    // 解釋下為什么shouldParkAfterFailedAcquire(p, node)返回false的時(shí)候不直接掛起線(xiàn)程:
    // => 是為了應(yīng)對(duì)在經(jīng)過(guò)這個(gè)方法后,node已經(jīng)是head的直接后繼節(jié)點(diǎn)了。剩下的讀者自己想想吧。
}

說(shuō)到這里,也就明白了,多看幾遍 final boolean acquireQueued(final Node node, int arg) 這個(gè)方法吧。自己推演下各個(gè)分支怎么走,哪種情況下會(huì)發(fā)生什么,走到哪里。

解鎖操作

最后,就是還需要介紹下喚醒的動(dòng)作了。我們知道,正常情況下,如果線(xiàn)程沒(méi)獲取到鎖,線(xiàn)程會(huì)被 LockSupport.park(this); 掛起停止,等待被喚醒。

// 喚醒的代碼還是比較簡(jiǎn)單的,你如果上面加鎖的都看懂了,下面都不需要看就知道怎么回事了
public void unlock() {
    sync.release(1);
}
public final boolean release(int arg) {
    // 往后看吧
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}
// 回到ReentrantLock看tryRelease方法
protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    // 是否完全釋放鎖
    boolean free = false;
    // 其實(shí)就是重入的問(wèn)題,如果c==0,也就是說(shuō)沒(méi)有嵌套鎖了,可以釋放了,否則還不能釋放掉
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}
/**
 * Wakes up node's successor, if one exists.
 *
 * @param node the node
 */
// 喚醒后繼節(jié)點(diǎn)
// 從上面調(diào)用處知道,參數(shù)node是head頭結(jié)點(diǎn)
private void unparkSuccessor(Node node) {
    /*
     * If status is negative (i.e., possibly needing signal) try
     * to clear in anticipation of signalling.  It is OK if this
     * fails or if status is changed by waiting thread.
     */
    int ws = node.waitStatus;
    // 如果head節(jié)點(diǎn)當(dāng)前waitStatus<0, 將其修改為0
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);
    /*
     * Thread to unpark is held in successor, which is normally
     * just the next node.  But if cancelled or apparently null,
     * traverse backwards from tail to find the actual
     * non-cancelled successor.
     */
    // 下面的代碼就是喚醒后繼節(jié)點(diǎn),但是有可能后繼節(jié)點(diǎn)取消了等待(waitStatus==1)
    // 從隊(duì)尾往前找,找到waitStatus<=0的所有節(jié)點(diǎn)中排在最前面的
    Node s = node.next;
    if (s == null || s.waitStatus > 0) {
        s = null;
        // 從后往前找,仔細(xì)看代碼,不必?fù)?dān)心中間有節(jié)點(diǎn)取消(waitStatus==1)的情況
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    if (s != null)
        // 喚醒線(xiàn)程
        LockSupport.unpark(s.thread);
}

喚醒線(xiàn)程以后,被喚醒的線(xiàn)程將從以下代碼中繼續(xù)往前走:

private final boolean parkAndCheckInterrupt() {
    LockSupport.park(this); // 剛剛線(xiàn)程被掛起在這里了
    return Thread.interrupted();
}
// 又回到這個(gè)方法了:acquireQueued(final Node node, int arg),這個(gè)時(shí)候,node的前驅(qū)是head了

好了,后面就不分析源碼了,剩下的還有問(wèn)題自己去仔細(xì)看看代碼吧。

總結(jié)

總結(jié)一下吧。

在并發(fā)環(huán)境下,加鎖和解鎖需要以下三個(gè)部件的協(xié)調(diào):

  1. 鎖狀態(tài)。我們要知道鎖是不是被別的線(xiàn)程占有了,這個(gè)就是 state 的作用,它為 0 的時(shí)候代表沒(méi)有線(xiàn)程占有鎖,可以去爭(zhēng)搶這個(gè)鎖,用 CAS 將 state 設(shè)為 1,如果 CAS 成功,說(shuō)明搶到了鎖,這樣其他線(xiàn)程就搶不到了,如果鎖重入的話(huà),state進(jìn)行 +1 就可以,解鎖就是減 1,直到 state 又變?yōu)?0,代表釋放鎖,所以 lock() 和 unlock() 必須要配對(duì)啊。然后喚醒等待隊(duì)列中的第一個(gè)線(xiàn)程,讓其來(lái)占有鎖。

  2. 線(xiàn)程的阻塞和解除阻塞。AQS 中采用了 LockSupport.park(thread) 來(lái)掛起線(xiàn)程,用 unpark 來(lái)喚醒線(xiàn)程。

  3. 阻塞隊(duì)列。因?yàn)闋?zhēng)搶鎖的線(xiàn)程可能很多,但是只能有一個(gè)線(xiàn)程拿到鎖,其他的線(xiàn)程都必須等待,這個(gè)時(shí)候就需要一個(gè) queue 來(lái)管理這些線(xiàn)程,AQS 用的是一個(gè) FIFO 的隊(duì)列,就是一個(gè)鏈表,每個(gè) node 都持有后繼節(jié)點(diǎn)的引用。AQS 采用了 CLH 鎖的變體來(lái)實(shí)現(xiàn),感興趣的讀者可以參考這篇文章 關(guān)于CLH的介紹,寫(xiě)得簡(jiǎn)單明了。

示例圖解析

下面屬于回顧環(huán)節(jié),用簡(jiǎn)單的示例來(lái)說(shuō)一遍,如果上面的有些東西沒(méi)看懂,這里還有一次幫助你理解的機(jī)會(huì)。

首先,第一個(gè)線(xiàn)程調(diào)用 reentrantLock.lock(),翻到最前面可以發(fā)現(xiàn),tryAcquire(1) 直接就返回 true 了,結(jié)束。只是設(shè)置了 state=1,連 head 都沒(méi)有初始化,更談不上什么阻塞隊(duì)列了。要是線(xiàn)程 1 調(diào)用 unlock() 了,才有線(xiàn)程 2 來(lái),那世界就太太太平了,完全沒(méi)有交集嘛,那我還要 AQS 干嘛。

如果線(xiàn)程 1 沒(méi)有調(diào)用 unlock() 之前,線(xiàn)程 2 調(diào)用了 lock(), 想想會(huì)發(fā)生什么?

線(xiàn)程 2 會(huì)初始化 head【new Node()】,同時(shí)線(xiàn)程 2 也會(huì)插入到阻塞隊(duì)列并掛起 (注意看這里是一個(gè) for 循環(huán),而且設(shè)置 head 和 tail 的部分是不 return 的,只有入隊(duì)成功才會(huì)跳出循環(huán))

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

首先,是線(xiàn)程 2 初始化 head 節(jié)點(diǎn),此時(shí) head==tail, waitStatus==0

Java JUC的核心類(lèi)AQS有什么用

然后線(xiàn)程 2 入隊(duì):

Java JUC的核心類(lèi)AQS有什么用

同時(shí)我們也要看此時(shí)節(jié)點(diǎn)的 waitStatus,我們知道 head 節(jié)點(diǎn)是線(xiàn)程 2 初始化的,此時(shí)的 waitStatus 沒(méi)有設(shè)置, java 默認(rèn)會(huì)設(shè)置為 0,但是到 shouldParkAfterFailedAcquire 這個(gè)方法的時(shí)候,線(xiàn)程 2 會(huì)把前驅(qū)節(jié)點(diǎn),也就是 head 的waitStatus設(shè)置為 -1。

那線(xiàn)程 2 節(jié)點(diǎn)此時(shí)的 waitStatus 是多少呢,由于沒(méi)有設(shè)置,所以是 0;

如果線(xiàn)程 3 此時(shí)再進(jìn)來(lái),直接插到線(xiàn)程 2 的后面就可以了,此時(shí)線(xiàn)程 3 的 waitStatus 是 0,到 shouldParkAfterFailedAcquire 方法的時(shí)候把前驅(qū)節(jié)點(diǎn)線(xiàn)程 2 的 waitStatus 設(shè)置為 -1。

Java JUC的核心類(lèi)AQS有什么用

到此,關(guān)于“Java JUC的核心類(lèi)AQS有什么用”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!

網(wǎng)站標(biāo)題:JavaJUC的核心類(lèi)AQS有什么用
本文路徑:http://www.chinadenli.net/article46/ihodhg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站策劃網(wǎng)站排名品牌網(wǎng)站制作App開(kāi)發(fā)建站公司網(wǎng)站維護(hù)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

商城網(wǎng)站建設(shè)