成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

原理剖析(第 005 篇)AQS工作原理分析

Aklman / 1033人閱讀

摘要:等到所有子線程都執(zhí)行完后即,會(huì)主調(diào)用線程,然后主調(diào)用線程就會(huì)從函數(shù)返回,繼續(xù)后余動(dòng)作。

原理剖析(第 005 篇)AQS工作原理分析

-

一、大致介紹
1、前面章節(jié)講解了一下CAS,簡(jiǎn)單講就是cmpxchg+lock的原子操作;
2、而在談到并發(fā)操作里面,我們不得不談到AQS,JDK的源碼里面好多并發(fā)的類都是通過(guò)Sync的內(nèi)部類繼承AQS而實(shí)現(xiàn)出五花八門的功能;
3、本章節(jié)就和大家分享分析一下AQS的工作原理; 
二、簡(jiǎn)單認(rèn)識(shí)AQS 2.1 何為AQS?
1、AQS是一個(gè)抽象類,類名為AbstractQueuedSynchronizer,抽象的都是一些公用的方法屬性,其自身是沒(méi)有實(shí)現(xiàn)任何同步接口的;

2、AQS定義了同步器中獲取鎖和釋放鎖,目的來(lái)讓自定義同步器組件來(lái)使用或重寫;

3、縱觀AQS的子類,絕大多數(shù)都是一個(gè)叫Sync的靜態(tài)內(nèi)部類來(lái)繼承AQS類,通過(guò)重寫AQS中的一些方法來(lái)實(shí)現(xiàn)自定義同步器;

4、AQS定義了兩種資源共享方式:EXCLUSIVE( 獨(dú)占式:每次僅有一個(gè)Thread能執(zhí)行 )、SHARED( 共享式:多個(gè)線程可同時(shí)執(zhí)行 );

5、AQS維護(hù)了一個(gè)FIFO的CLH鏈表隊(duì)列,且該隊(duì)列不支持基于優(yōu)先級(jí)的同步策略;
2.2 AQS的state關(guān)鍵詞
1、private volatile int state:維護(hù)了一個(gè)volatile的int類型的state字段,該字段是實(shí)現(xiàn)AQS的核心關(guān)鍵詞; 

2、通過(guò)getState、setState、compareAndSetState方法類獲取、設(shè)置更新state值;

3、該字段在不同的并發(fā)類中起著不同的紐帶作用,下面會(huì)接著講到state字段的一些應(yīng)用場(chǎng)景;
2.3 Node的waitStatus關(guān)鍵詞
1、正常默認(rèn)的狀態(tài)值為0;

2、對(duì)于釋放操作的時(shí)候,前一個(gè)結(jié)點(diǎn)有喚醒后一個(gè)結(jié)點(diǎn)的任務(wù);

3、當(dāng)前結(jié)點(diǎn)的前置結(jié)點(diǎn)waitStatus > 0,則結(jié)點(diǎn)處于CANCELLED狀態(tài),應(yīng)該需要踢出隊(duì)列;

4、當(dāng)前結(jié)點(diǎn)的前置結(jié)點(diǎn)waitStatus = 0,則需要將前置結(jié)點(diǎn)改為SIGNAL狀態(tài);
2.4 CLH隊(duì)列
1、隊(duì)列模型:
      +------+  prev +------+  prev +------+
      |      | <---- |      | <---- |      |  
 head | Node |  next | Node |  next | Node |  tail
      |      | ----> |      | ----> |      |  
      +------+       +------+       +------+

2、鏈表結(jié)構(gòu),在頭尾結(jié)點(diǎn)中,需要特別指出的是頭結(jié)點(diǎn)是一個(gè)空對(duì)象結(jié)點(diǎn),無(wú)任何意義,即傀儡結(jié)點(diǎn);
      
3、每一個(gè)Node結(jié)點(diǎn)都維護(hù)了一個(gè)指向前驅(qū)的指針和指向后驅(qū)的指針,結(jié)點(diǎn)與結(jié)點(diǎn)之間相互關(guān)聯(lián)構(gòu)成鏈表;

4、入隊(duì)在尾,出隊(duì)在頭,出隊(duì)后需要激活該出隊(duì)結(jié)點(diǎn)的后繼結(jié)點(diǎn),若后繼結(jié)點(diǎn)為空或后繼結(jié)點(diǎn)waitStatus>0,則從隊(duì)尾向前遍歷取waitStatus<0的觸發(fā)阻塞喚醒;
2.5 state在AQS簡(jiǎn)單應(yīng)用舉例
1、CountDownLatch,簡(jiǎn)單大致意思為:A組線程等待另外B組線程,B組線程執(zhí)行完了,A組線程才可以執(zhí)行;
   state初始化假設(shè)為N,后續(xù)每countDown()一次,state會(huì)CAS減1。
   等到所有子線程都執(zhí)行完后(即state=0),會(huì)unpark()主調(diào)用線程,然后主調(diào)用線程就會(huì)從await()函數(shù)返回,繼續(xù)后余動(dòng)作。

2、ReentrantLock,簡(jiǎn)單大致意思為:獨(dú)占式鎖的類;
   state初始化為0,表示未鎖定狀態(tài),然后每lock()時(shí)調(diào)用tryAcquire()使state加1,
   其他線程再tryAcquire()時(shí)就會(huì)失敗,直到A線程unlock()到state=0(即釋放鎖)為止,其它線程才有機(jī)會(huì)獲取該鎖;

3、Semaphore,簡(jiǎn)單大致意思為:A、B、C、D線程同時(shí)爭(zhēng)搶資源,目前卡槽大小為2,若A、B正在執(zhí)行且未執(zhí)行完,那么C、D線程在門外等著,一旦A、B有1個(gè)執(zhí)行完了,那么C、D就會(huì)競(jìng)爭(zhēng)看誰(shuí)先執(zhí)行;
   state初始值假設(shè)為N,后續(xù)每tryAcquire()一次,state會(huì)CAS減1,當(dāng)state為0時(shí)其它線程處于等待狀態(tài),
   直到state>0且
2.6 常用重要的方法
1、protected boolean isHeldExclusively()
   // 需要被子類實(shí)現(xiàn)的方法,調(diào)用該方法的線程是否持有獨(dú)占鎖,一般用到了condition的時(shí)候才需要實(shí)現(xiàn)此方法

2、protected boolean tryAcquire(int arg)
   // 需要被子類實(shí)現(xiàn)的方法,獨(dú)占方式嘗試獲取鎖,獲取鎖成功后返回true,獲取鎖失敗后返回false

3、protected boolean tryRelease(int arg)  
   // 需要被子類實(shí)現(xiàn)的方法,獨(dú)占方式嘗試釋放鎖,釋放鎖成功后返回true,釋放鎖失敗后返回false
   
4、protected int tryAcquireShared(int arg)  
   // 需要被子類實(shí)現(xiàn)的方法,共享方式嘗試獲取鎖,獲取鎖成功后返回正數(shù)1,獲取鎖失敗后返回負(fù)數(shù)-1
   
5、protected boolean tryReleaseShared(int arg)   
   // 需要被子類實(shí)現(xiàn)的方法,共享方式嘗試釋放鎖,釋放鎖成功后返回正數(shù)1,釋放鎖失敗后返回負(fù)數(shù)-1
   
6、final boolean acquireQueued(final Node node, int arg)
   // 對(duì)于進(jìn)入隊(duì)尾的結(jié)點(diǎn),檢測(cè)自己可以休息了,如果可以修改則進(jìn)入SIGNAL狀態(tài)且進(jìn)入park()阻塞狀態(tài)

7、private Node addWaiter(Node mode)
   // 添加結(jié)點(diǎn)到鏈表隊(duì)尾

8、private Node enq(final Node node)
   // 如果addWaiter嘗試添加隊(duì)尾失敗,則再次調(diào)用enq此方法自旋將結(jié)點(diǎn)加入隊(duì)尾

9、private static boolean shouldParkAfterFailedAcquire(Node pred, Node node)
   // 檢測(cè)結(jié)點(diǎn)狀態(tài),如果可以休息的話則設(shè)置waitStatus=SIGNAL并調(diào)用LockSupport.park休息;

10、private void unparkSuccessor(Node node)   
   // 釋放鎖時(shí),該方法需要負(fù)責(zé)喚醒后繼節(jié)點(diǎn)
2.7 設(shè)計(jì)與實(shí)現(xiàn)偽代碼
1、獲取獨(dú)占鎖:
    public final void acquire(int arg) {
        if (!tryAcquire(arg) && 
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

    acquire{
        如果嘗試獲取獨(dú)占鎖失敗的話( 嘗試獲取獨(dú)占鎖的各種方式由AQS的子類實(shí)現(xiàn) ),
        那么就新增獨(dú)占鎖結(jié)點(diǎn)通過(guò)自旋操作加入到隊(duì)列中,并且根據(jù)結(jié)點(diǎn)中的waitStatus來(lái)決定是否調(diào)用LockSupport.park進(jìn)行休息
    }
    
    
2、釋放獨(dú)占鎖:
    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
    
    release{
        如果嘗試釋放獨(dú)占鎖成功的話( 嘗試釋放獨(dú)占鎖的各種方式由AQS的子類實(shí)現(xiàn) ),
        那么取出頭結(jié)點(diǎn)并根據(jù)結(jié)點(diǎn)waitStatus來(lái)決定是否有義務(wù)喚醒其后繼結(jié)點(diǎn)
    }

3、獲取共享鎖:
    public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }
    
    acquireShared{
        如果嘗試獲取共享鎖失敗的話( 嘗試獲取共享鎖的各種方式由AQS的子類實(shí)現(xiàn) ),
        那么新增共享鎖結(jié)點(diǎn)通過(guò)自旋操作加入到隊(duì)尾中,并且根據(jù)結(jié)點(diǎn)中的waitStatus來(lái)決定是否調(diào)用LockSupport.park進(jìn)行休息
    }

4、釋放共享鎖:
    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }
    
    releaseShared{
        如果嘗試釋放共享鎖失敗的話( 嘗試釋放共享鎖的各種方式由AQS的子類實(shí)現(xiàn) ),
        那么通過(guò)自旋操作喚完成阻塞線程的喚起操作
    }
三、舉例ReentrantLock 3.1、ReentrantLock
1、在分析AQS源碼前,我們需要依賴一個(gè)載體來(lái)說(shuō),畢竟AQS的一些方法都是空方法且拋異常的,所以單講AQS不太生動(dòng)形象;

2、因此我們決定采用ReentrantLock來(lái)講解,其他都大致差不多,因?yàn)榱私饬艘粋€(gè),其他都可以依葫蘆畫瓢秒懂;
3.2、ReentrantLock生活細(xì)節(jié)化理解
比如我們天天在外面吃快餐,我就以吃快餐為例生活化闡述該ReentrantLock原理:

1、場(chǎng)景:餐廳只有一個(gè)排隊(duì)的走廊,只有一個(gè)打飯菜的師傅;

2、開飯時(shí)間點(diǎn),大家都爭(zhēng)先恐后的去吃飯,因此排上了隊(duì),挨個(gè)挨個(gè)排隊(duì)打飯菜,任何一個(gè)人只要排到了打飯師傅的前面,都可以打到飯菜;

3、但是有時(shí)候隊(duì)很長(zhǎng),有些人之間的關(guān)系是家屬關(guān)系,如果后來(lái)的人看到自己家屬正在打飯菜,這個(gè)時(shí)候可以不用排隊(duì)直接跑到前面打飯菜;

4、總之大家都挨個(gè)挨個(gè)排隊(duì)打飯,有家屬關(guān)系的直接跑到前面打飯菜;

5、到此打止,1、2、3、4可以認(rèn)為是一種公平方式的獨(dú)占鎖,3可以理解為重入鎖;

5、但是呢,還有那么些緊急趕時(shí)間的人,而且又跟排隊(duì)的人沒(méi)半點(diǎn)瓜葛,來(lái)餐廳時(shí)剛好看到師傅剛剛打完一個(gè)人的飯菜,于是插入去打飯菜敢時(shí)間;

6、如果敢時(shí)間人的來(lái)的時(shí)候發(fā)現(xiàn)師傅還在打飯菜,那么就只得乖乖的排隊(duì)等候打飯菜咯;

7、到此打止,1、2、5、6可以認(rèn)為是一種非公平方式的獨(dú)占鎖;
四、源碼分析ReentrantLock 4.1、ReentrantLock構(gòu)造器
1、構(gòu)造器源碼:
    /**
     * Creates an instance of {@code ReentrantLock}.
     * This is equivalent to using {@code ReentrantLock(false)}.
     */
    public ReentrantLock() {
        sync = new NonfairSync();
    }

    /**
     * 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();
    }
    
2、默認(rèn)構(gòu)造方法為非公平鎖,帶參構(gòu)造方法還可通過(guò)傳入變量還決定調(diào)用方是使用公平鎖還是非公平鎖;    
4.2、Sync同步器
1、AQS --> Sync ---> FairSync // 公平鎖
                  |
                  |> NonfairSync // 非公平鎖
                  
2、ReentrantLock內(nèi)的同步器都是通過(guò)Sync抽象接口來(lái)操作調(diào)用關(guān)系的,細(xì)看會(huì)發(fā)現(xiàn)基本上都是通過(guò)sync.xxx之類的這種調(diào)用方式的;
4.3、lock()
1、源碼:
    public void lock() {
        sync.lock();
    }
    
    // FairSync 公平鎖調(diào)用方式
    final void lock() {
        acquire(1); // 嘗試獲取獨(dú)占鎖
    }    
    
    // NonfairSync 非公平鎖調(diào)用方式
    final void lock() {
        if (compareAndSetState(0, 1)) // 首先判斷state資源是否為0,如果恰巧為0則表明目前沒(méi)有線程占用鎖,則利用CAS占有鎖
            setExclusiveOwnerThread(Thread.currentThread()); // 當(dāng)獨(dú)占鎖之后則將設(shè)置exclusiveOwnerThread為當(dāng)前線程
        else
            acquire(1); // 若CAS占用鎖失敗的話,則再嘗試獲取獨(dú)占鎖
    }
    
2、這里的區(qū)別就是非公平鎖在調(diào)用lock時(shí)首先檢測(cè)了是否通過(guò)CAS獲取鎖,發(fā)現(xiàn)鎖一旦空著的話,則搶先一步占為己有,
   不管有沒(méi)有阻塞隊(duì)列,只要當(dāng)前線程來(lái)的時(shí)候發(fā)現(xiàn)state資源沒(méi)被占用那么當(dāng)前線程就搶先一步試一下CAS,CAS失敗了它才去排隊(duì);
4.4、acquire(int)
1、源碼:
    public final void acquire(int arg) {
        if (!tryAcquire(arg) && // 嘗試獲取鎖資源,若獲取到資源的話則線程直接返回,此方法由AQS的具體子類實(shí)現(xiàn)
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 否則獲取資源失敗的話,那么就進(jìn)入等待隊(duì)列
            selfInterrupt();
    }
    
2、該方法是獨(dú)占模式下線程獲取state共享資源的入口,如果獲取到資源的話就返回,否則創(chuàng)建獨(dú)占模式結(jié)點(diǎn)加入阻塞隊(duì)列,直到獲取到共享資源;

3、而且這里需要加上自我中斷判斷,主要是因?yàn)榫€程在等待過(guò)程中被中斷的話,它是不響應(yīng)的,那么就只有等到線程獲取到資源后通過(guò)自我判斷將這個(gè)判斷后續(xù)補(bǔ)上;

4、獨(dú)占模式的該方法,正常情況下只要沒(méi)有獲取到鎖,該方法一直處于阻塞狀態(tài),獲取到了則跳出該方法區(qū);
4.5、tryAcquire(int)
1、公平鎖tryAcquire源碼:
    // FairSync 公平鎖的 tryAcquire 方法
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState(); // 獲取鎖資源的最新內(nèi)存值
        if (c == 0) { // 當(dāng)state=0,說(shuō)明鎖資源目前還沒(méi)有被任何線程被占用
            if (!hasQueuedPredecessors() && // 檢查線程是否有阻塞隊(duì)列
                compareAndSetState(0, acquires)) { // 如果沒(méi)有阻塞隊(duì)列,則通過(guò)CAS操作獲取鎖資源
                setExclusiveOwnerThread(current); // 沒(méi)有阻塞隊(duì)列,且CAS又成功獲取鎖資源,則設(shè)置獨(dú)占線程對(duì)象為當(dāng)前線程
                return true; // 返回標(biāo)志,告訴上層該線程已經(jīng)獲取到了鎖資源
            }
        }
        // 執(zhí)行到此,鎖資源值不為0,說(shuō)明已經(jīng)有線程正在占用這鎖資源
        else if (current == getExclusiveOwnerThread()) { // 既然鎖已經(jīng)被占用,則看看占用鎖的線程是不是當(dāng)前線程
            int nextc = c + acquires; // 如果占用的鎖的線程是當(dāng)前線程的話,則為重入鎖概念,狀態(tài)值做加1操作
            // int類型值小于0,是因?yàn)樵搃nt類型的state狀態(tài)值溢出了,溢出了的話那得說(shuō)明這個(gè)鎖有多難獲取啊,可能出問(wèn)題了
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true; // 返回成功標(biāo)志,告訴上層該線程已經(jīng)獲取到了鎖資源
        }
        return false; // 返回失敗標(biāo)志,告訴上層該線程沒(méi)有獲取到鎖資源
    }

2、非公平鎖tryAcquire源碼:
    // NonfairSync 非公平鎖的 tryAcquire 方法
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires); // 調(diào)用父類的非公平獲取鎖資源方法
    }    

    // NonfairSync 非公平鎖父類 Sync 類的 nonfairTryAcquire 方法    
    final boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState(); // 獲取鎖資源的最新內(nèi)存值
        if (c == 0) { // 當(dāng)state=0,說(shuō)明鎖資源目前還沒(méi)有被任何線程被占用
            if (compareAndSetState(0, acquires)) { // 先不管三七二十一,先嘗試通過(guò)CAS操作獲取鎖資源
                setExclusiveOwnerThread(current); // CAS一旦成功獲取鎖資源,則設(shè)置獨(dú)占線程對(duì)象為當(dāng)前線程
                return true;// 返回成功標(biāo)志,告訴上層該線程已經(jīng)獲取到了鎖資源
            }
        }
        // 執(zhí)行到此,鎖資源值不為0,說(shuō)明已經(jīng)有線程正在占用這鎖資源
        else if (current == getExclusiveOwnerThread()) { // 既然鎖已經(jīng)被占用,則看看占用鎖的線程是不是當(dāng)前線程
            int nextc = c + acquires; // 如果占用的鎖的線程是當(dāng)前線程的話,則為重入鎖概念,狀態(tài)值做加1操作
            // int類型值小于0,是因?yàn)樵搃nt類型的state狀態(tài)值溢出了,溢出了的話那得說(shuō)明這個(gè)鎖有多難獲取啊,可能出問(wèn)題了
            if (nextc < 0) // overflow
                throw new Error("Maximum lock count exceeded");
            setState(nextc); // 
            return true; // 返回成功標(biāo)志,告訴上層該線程已經(jīng)獲取到了鎖資源
        }
        return false; // 返回失敗標(biāo)志,告訴上層該線程沒(méi)有獲取到鎖資源
    }    

3、tryAcquire方法是AQS的子類實(shí)現(xiàn)的,也就是ReentrantLock的兩個(gè)靜態(tài)內(nèi)部類實(shí)現(xiàn)的,目的就是通過(guò)CAS嘗試獲取鎖資源,
   獲取鎖資源成功則返回true,獲取鎖資源失敗則返回false; 
4.6、addWaiter(Node)
1、源碼:
    /**
     * Creates and enqueues node for current thread and given mode.
     *
     * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
     * @return the new node
     */
    private Node addWaiter(Node mode) {
        // 按照給定的mode模式創(chuàng)建新的結(jié)點(diǎn),模式有兩種:Node.EXCLUSIVE獨(dú)占模式、Node.SHARED共享模式;
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail; // 將先隊(duì)尾結(jié)點(diǎn)賦值給臨時(shí)變量
        if (pred != null) { // 如果pred不為空,說(shuō)明該隊(duì)列已經(jīng)有結(jié)點(diǎn)了
            node.prev = pred;
            if (compareAndSetTail(pred, node)) { // 通過(guò)CAS嘗試將node結(jié)點(diǎn)設(shè)置為隊(duì)尾結(jié)點(diǎn)
                pred.next = node;
                return node;
            }
        }
        // 執(zhí)行到此,說(shuō)明隊(duì)尾沒(méi)有元素,則進(jìn)入自旋首先設(shè)置頭結(jié)點(diǎn),然后將此新建結(jié)點(diǎn)添加到隊(duì)尾
        enq(node); // 進(jìn)入自旋添加node結(jié)點(diǎn)
        return node;
    }
    
2、    addWaiter通過(guò)傳入不同的模式來(lái)創(chuàng)建新的結(jié)點(diǎn)嘗試加入到隊(duì)列尾部,如果由于并發(fā)導(dǎo)致添加結(jié)點(diǎn)到隊(duì)尾失敗的話那么就進(jìn)入自旋將結(jié)點(diǎn)加入隊(duì)尾;
4.7、enq(Node)
1、源碼:
    /**
     * Inserts node into queue, initializing if necessary. See picture above.
     * @param node the node to insert
     * @return node"s predecessor
     */
    private Node enq(final Node node) {
        for (;;) { // 自旋的死循環(huán)操作方式
            Node t = tail;
            // 因?yàn)槭亲孕绞?,首次鏈表?duì)列tail肯定為空,但是后續(xù)鏈表有數(shù)據(jù)后就不會(huì)為空了
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node())) // 隊(duì)列為空時(shí),則創(chuàng)建一個(gè)空對(duì)象結(jié)點(diǎn)作為頭結(jié)點(diǎn),無(wú)意思,可認(rèn)為傀儡結(jié)點(diǎn)
                    tail = head; // 空隊(duì)列的話,頭尾都指向同一個(gè)對(duì)象
            } else {
                // 進(jìn)入 else 方法里面,說(shuō)明鏈表隊(duì)列已經(jīng)有結(jié)點(diǎn)了
                node.prev = t;
                // 因?yàn)榇嬖诓l(fā)操作,通過(guò)CAS嘗試將新加入的node結(jié)點(diǎn)設(shè)置為隊(duì)尾結(jié)點(diǎn)
                if (compareAndSetTail(t, node)) { 
                    // 如果node設(shè)置隊(duì)尾結(jié)點(diǎn)成功,則將之前的舊的對(duì)象尾結(jié)點(diǎn)t的后繼結(jié)點(diǎn)指向node,node的前驅(qū)結(jié)點(diǎn)也設(shè)置為t
                    t.next = node;
                    return t;
                }
            }
            
            // 如果執(zhí)行到這里,說(shuō)明上述兩個(gè)CAS操作任何一個(gè)失敗的話,該方法是不會(huì)放棄的,因?yàn)槭亲孕僮?,再次循環(huán)繼續(xù)入隊(duì)
        }
    }

2、enq通過(guò)自旋這種死循環(huán)的操作方式,來(lái)確保結(jié)點(diǎn)正確的添加到隊(duì)列尾部,通過(guò)CAS操作如果頭部為空則添加傀儡空結(jié)點(diǎn),然后在循環(huán)添加隊(duì)尾結(jié)點(diǎn);
4.8、compareAndSetHead/compareAndSetTail
1、源碼:
    /**
     * CAS head field. Used only by enq.
     */
    private final boolean compareAndSetHead(Node update) {
        return unsafe.compareAndSwapObject(this, headOffset, null, update);
    }
    
    /**
     * CAS tail field. Used only by enq.
     */
    private final boolean compareAndSetTail(Node expect, Node update) {
        return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
    }    

2、CAS操作,設(shè)置頭結(jié)點(diǎn)、尾結(jié)點(diǎn);
4.9、acquireQueued(Node, int)
1、源碼:
    /**
     * Acquires in exclusive uninterruptible mode for thread already in
     * queue. Used by condition wait methods as well as acquire.
     *
     * @param node the node
     * @param arg the acquire argument
     * @return {@code true} if interrupted while waiting
     */
    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) { // 自旋的死循環(huán)操作方式
                final Node p = node.predecessor(); // 如果新建結(jié)點(diǎn)的前驅(qū)結(jié)點(diǎn)是頭結(jié)點(diǎn)
                // 如果前驅(qū)結(jié)點(diǎn)為頭結(jié)點(diǎn),那么該結(jié)點(diǎn)則是老二,僅次于老大,也希望嘗試去獲取一下鎖,萬(wàn)一頭結(jié)點(diǎn)恰巧剛剛釋放呢?希望還是要有的,萬(wàn)一實(shí)現(xiàn)了呢。。。
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    // 拿到鎖資源后,則該node結(jié)點(diǎn)升級(jí)做頭結(jié)點(diǎn),且設(shè)置后繼結(jié)點(diǎn)指針為空,便于GC回收
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) && // 根據(jù)前驅(qū)結(jié)點(diǎn)看看是否需要休息一會(huì)兒
                    parkAndCheckInterrupt()) // 阻塞操作,正常情況下,獲取不到鎖,代碼就在該方法停止了,直到被喚醒
                    interrupted = true;
                    
                // 如果執(zhí)行到這里,說(shuō)明嘗試休息失敗了,因?yàn)槭亲孕僮?,所以還會(huì)再次循環(huán)繼續(xù)操作判斷
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

2、acquireQueued也是采用一個(gè)自旋的死循環(huán)操作方式,只有頭結(jié)點(diǎn)才能嘗試獲取鎖資源,其余的結(jié)點(diǎn)挨個(gè)挨個(gè)在那里等待修改,等待被喚醒,等待機(jī)會(huì)成為頭結(jié)點(diǎn);
   而新添加的node結(jié)點(diǎn)也自然逃不過(guò)如此命運(yùn),先看看是否頭結(jié)點(diǎn),然后再看看是否能休息;
4.10、shouldParkAfterFailedAcquire(Node, Node)
1、源碼:
    /**
     * 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
     */
    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus; // 獲取前驅(qū)結(jié)點(diǎn)的狀態(tài)值
        if (ws == Node.SIGNAL) // 若前驅(qū)結(jié)點(diǎn)的狀態(tài)為SIGNAL狀態(tài)的話,那么該結(jié)點(diǎn)就不要想事了,直接返回true準(zhǔn)備休息
            /*
             * This node has already set status asking a release
             * to signal it, so it can safely park.
             */
            return true;
        if (ws > 0) {
            /*
             * Predecessor was cancelled. Skip over predecessors and
             * indicate retry.
             */
            // 若前驅(qū)結(jié)點(diǎn)的狀態(tài)為CANCELLED狀態(tài)的話,那么就一直向前遍歷,直到找到一個(gè)不為CANCELLED狀態(tài)的結(jié)點(diǎn)
            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.
             */
             // 剩下的結(jié)點(diǎn)狀態(tài),則設(shè)置其為SIGNAL狀態(tài),然后返回false標(biāo)志等外層循環(huán)再次判斷
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

2、shouldParkAfterFailedAcquire主要是檢測(cè)前驅(qū)結(jié)點(diǎn)狀態(tài),前驅(qū)結(jié)點(diǎn)為SIGNAL的話,則新結(jié)點(diǎn)可以安安心心休息了;
   如果前驅(qū)結(jié)點(diǎn)大于零,說(shuō)明前驅(qū)結(jié)點(diǎn)處于CANCELLED狀態(tài),那么則以入?yún)red前驅(qū)為起點(diǎn),一直往前找,直到找到最近一個(gè)正常等待狀態(tài)的結(jié)點(diǎn);
   如果前驅(qū)結(jié)點(diǎn)小于零,那么就將前驅(qū)結(jié)點(diǎn)設(shè)置為SIGNAL狀態(tài),然后返回false依賴acquireQueued的自旋再次判斷是否需要進(jìn)行休息;
4.11、parkAndCheckInterrupt()
1、源碼:
    /**
     * Convenience method to park and then check if interrupted
     *
     * @return {@code true} if interrupted
     */
    private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this); // 阻塞等待
        return Thread.interrupted(); // 被喚醒后查看是否有被中斷過(guò)否?
    }

2、parkAndCheckInterrupt首先調(diào)用park讓線程進(jìn)入等待狀態(tài),然后當(dāng)park阻塞被喚醒后,再次檢測(cè)是否曾經(jīng)被中斷過(guò);
   而被喚醒有兩種情況,一個(gè)是利用unpark喚醒,一個(gè)是利用interrupt喚醒;
4.12、unlock()
1、源碼:
    public void unlock() {
        sync.release(1); // 
    }

2、unlock釋放鎖資源,一般都是在finally中被調(diào)用,防止當(dāng)臨界區(qū)因?yàn)槿魏萎惓r(shí)怕鎖不被釋放;
   而釋放鎖不像獲取鎖lock的實(shí)現(xiàn)多色多樣,沒(méi)有所謂公平或不公平,就是規(guī)規(guī)矩矩的釋放資源而已;
4.13、release(int)
1、源碼:
    /**
     * Releases in exclusive mode.  Implemented by unblocking one or
     * more threads if {@link #tryRelease} returns true.
     * This method can be used to implement method {@link Lock#unlock}.
     *
     * @param arg the release argument.  This value is conveyed to
     *        {@link #tryRelease} but is otherwise uninterpreted and
     *        can represent anything you like.
     * @return the value returned from {@link #tryRelease}
     */
    public final boolean release(int arg) {
        if (tryRelease(arg)) { // 嘗試釋放鎖資源,此方法由AQS的具體子類實(shí)現(xiàn)
            Node h = head;
            if (h != null && h.waitStatus != 0) // 從頭結(jié)點(diǎn)開始,喚醒后繼結(jié)點(diǎn)
                unparkSuccessor(h); // 踢出CANCELLED狀態(tài)結(jié)點(diǎn),然后喚醒后繼結(jié)點(diǎn)
            return true;
        }
        return false;
    }

2、release嘗試釋放鎖,并且有義務(wù)移除CANCELLED狀態(tài)的結(jié)點(diǎn),還有義務(wù)喚醒后繼結(jié)點(diǎn)繼續(xù)運(yùn)行獲取鎖資源;
4.14、tryRelease(int)
1、源碼:
    // NonfairSync 和 FairSync 的父類 Sync 類的 tryRelease 方法    
    protected final boolean tryRelease(int releases) {
        int c = getState() - releases; // 獲取鎖資源值并做減1操作
        if (Thread.currentThread() != getExclusiveOwnerThread()) // 查看當(dāng)前線程是否和持有鎖的線程是不是同一個(gè)線程
            // 正常情況下,需要釋放的線程肯定是持有鎖的線程,否則不就亂套了,肯定哪里出問(wèn)題了,所以拋出異常
            throw new IllegalMonitorStateException(); 
        boolean free = false;
        if (c == 0) { // 若此時(shí)鎖資源值做減法操作后正好是0,則所有鎖資源已經(jīng)釋放干凈,因此持有鎖的變量也置為空
            free = true;
            setExclusiveOwnerThread(null);
        }
        setState(c); // 若此時(shí)做減法操作還沒(méi)有歸零,那么這種情況就是那種重入鎖,需要重重釋放后才行
        return free;
    }

2、tryRelease主要通過(guò)CAS操作對(duì)state鎖資源進(jìn)行減1操作;
4.15、unparkSuccessor(Node)
1、源碼:
    /**
     * Wakes up node"s successor, if one exists.
     *
     * @param node the node
     */
    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.
         */
        // 該node一般都是傳入head進(jìn)來(lái),也就是說(shuō),需要釋放頭結(jié)點(diǎn),也就是當(dāng)前結(jié)點(diǎn)需要釋放鎖操作,順便喚醒后繼結(jié)點(diǎn)
        int ws = node.waitStatus;
        if (ws < 0) // 若結(jié)點(diǎn)狀態(tài)值小于0,則歸零處理,通過(guò)CAS歸零,允許失敗,但是不管怎么著,仍然要往下走去喚醒后繼結(jié)點(diǎn)
            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.
         */
        Node s = node.next; // 取出后繼結(jié)點(diǎn),這個(gè)時(shí)候一般都是Head后面的一個(gè)結(jié)點(diǎn),所以一般都是老二
        if (s == null || s.waitStatus > 0) { // 若后繼結(jié)點(diǎn)為空或者后繼結(jié)點(diǎn)已經(jīng)處于CANCELLED狀態(tài)的話
            s = null;
            // 那么從隊(duì)尾向前遍歷,直到找到一個(gè)小于等于0的結(jié)點(diǎn)
            // 這里為什么要從隊(duì)尾向前尋找?
            // * 因?yàn)樵谶@個(gè)隊(duì)列中,任何一個(gè)結(jié)點(diǎn)都有可能被中斷,只是有可能,并不代表絕對(duì)的,但有一點(diǎn)是確定的,
            // * 被中斷的結(jié)點(diǎn)會(huì)將結(jié)點(diǎn)的狀態(tài)設(shè)置為CANCELLED狀態(tài),標(biāo)識(shí)這個(gè)結(jié)點(diǎn)在將來(lái)的某個(gè)時(shí)刻會(huì)被踢出;
            // * 踢出隊(duì)列的規(guī)則很簡(jiǎn)單,就是該結(jié)點(diǎn)的前驅(qū)結(jié)點(diǎn)不會(huì)指向它,而是會(huì)指向它的后面的一個(gè)非CANCELLED狀態(tài)的結(jié)點(diǎn);
            // * 而這個(gè)將被踢出的結(jié)點(diǎn),它的next指針將會(huì)指向它自己;
            // * 所以設(shè)想一下,如果我們從head往后找,一旦發(fā)現(xiàn)這么一個(gè)處于CANCELLED狀態(tài)的結(jié)點(diǎn),那么for循環(huán)豈不是就是死循環(huán)了;
            // * 但是所有的這些結(jié)點(diǎn)當(dāng)中,它們的prev前驅(qū)結(jié)點(diǎn)還是沒(méi)有被誰(shuí)動(dòng)過(guò),所以從tail結(jié)點(diǎn)向前遍歷最穩(wěn)妥

            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            LockSupport.unpark(s.thread); // 喚醒線程
    }

2、unparkSuccessor主要是踢出CANCELLED狀態(tài)結(jié)點(diǎn),然后喚醒后繼結(jié)點(diǎn);
   但是這個(gè)喚醒的后繼結(jié)點(diǎn)為空的話,那么則從隊(duì)尾一直向前循環(huán)查找小于等于零狀態(tài)的結(jié)點(diǎn)并調(diào)用unpark喚醒;
五、總結(jié)
1、分析了這么多,感覺(jué)是不是有一種豁然開朗的感覺(jué),原來(lái)大家傳的神乎其神的AQS是不是沒(méi)有想象中那么難以理解;

2、在這里我簡(jiǎn)要總結(jié)一下AQS的流程的一些特性:
    ? 關(guān)鍵獲取鎖、釋放鎖操作由AQS子類實(shí)現(xiàn):acquire-release、acquireShared-releaseShared;
    ? 維護(hù)了一個(gè)FIFO鏈表結(jié)構(gòu)的隊(duì)列,通過(guò)自旋方式將新結(jié)點(diǎn)添加到隊(duì)尾;
    ? 添加結(jié)點(diǎn)時(shí)會(huì)從前驅(qū)結(jié)點(diǎn)向前遍歷,跳過(guò)那些處于CANCELLED狀態(tài)的結(jié)點(diǎn);
    ? 釋放結(jié)點(diǎn)時(shí)會(huì)從隊(duì)尾向前遍歷,踢出CANCELLED狀態(tài)的結(jié)點(diǎn),然后喚醒后繼結(jié)點(diǎn);

3、其實(shí)當(dāng)了解了AQS后,這里以ReentrantLock為載體分析了一下,那么再去分析CountDownLatch、Semaphore、ReentrantReadWriteLock等那些集成AQS而實(shí)現(xiàn)不同功能的模塊就會(huì)順利很多;
六、下載地址

https://gitee.com/ylimhhmily/SpringCloudTutorial.git

SpringCloudTutorial交流QQ群: 235322432

SpringCloudTutorial交流微信群: 微信溝通群二維碼圖片鏈接

歡迎關(guān)注,您的肯定是對(duì)我最大的支持!!!

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/71084.html

相關(guān)文章

  • 原理剖析 004 )CAS工作原理分析

    摘要:原理剖析第篇工作原理分析一大致介紹關(guān)于多線程競(jìng)爭(zhēng)鎖方面,大家都知道有個(gè)和,也正是這兩個(gè)東西才引申出了大量的線程安全類,鎖類等功能而隨著現(xiàn)在的硬件廠商越來(lái)越高級(jí),在硬件層面提供大量并發(fā)原語(yǔ)給我們層面的開發(fā)帶來(lái)了莫大的利好本章節(jié)就和大家分享分 原理剖析(第 004 篇)CAS工作原理分析 - 一、大致介紹 1、關(guān)于多線程競(jìng)爭(zhēng)鎖方面,大家都知道有個(gè)CAS和AQS,也正是這兩個(gè)東西才引申出了大...

    leanote 評(píng)論0 收藏0
  • 高并發(fā)

    摘要:表示的是兩個(gè),當(dāng)其中任意一個(gè)計(jì)算完并發(fā)編程之是線程安全并且高效的,在并發(fā)編程中經(jīng)常可見它的使用,在開始分析它的高并發(fā)實(shí)現(xiàn)機(jī)制前,先講講廢話,看看它是如何被引入的。電商秒殺和搶購(gòu),是兩個(gè)比較典型的互聯(lián)網(wǎng)高并發(fā)場(chǎng)景。 干貨:深度剖析分布式搜索引擎設(shè)計(jì) 分布式,高可用,和機(jī)器學(xué)習(xí)一樣,最近幾年被提及得最多的名詞,聽名字多牛逼,來(lái),我們一步一步來(lái)?yè)羝魄皟蓚€(gè)名詞,今天我們首先來(lái)說(shuō)說(shuō)分布式。 探究...

    supernavy 評(píng)論0 收藏0
  • 高并發(fā)

    摘要:表示的是兩個(gè),當(dāng)其中任意一個(gè)計(jì)算完并發(fā)編程之是線程安全并且高效的,在并發(fā)編程中經(jīng)??梢娝氖褂?,在開始分析它的高并發(fā)實(shí)現(xiàn)機(jī)制前,先講講廢話,看看它是如何被引入的。電商秒殺和搶購(gòu),是兩個(gè)比較典型的互聯(lián)網(wǎng)高并發(fā)場(chǎng)景。 干貨:深度剖析分布式搜索引擎設(shè)計(jì) 分布式,高可用,和機(jī)器學(xué)習(xí)一樣,最近幾年被提及得最多的名詞,聽名字多牛逼,來(lái),我們一步一步來(lái)?yè)羝魄皟蓚€(gè)名詞,今天我們首先來(lái)說(shuō)說(shuō)分布式。 探究...

    ddongjian0000 評(píng)論0 收藏0
  • 高并發(fā)

    摘要:表示的是兩個(gè),當(dāng)其中任意一個(gè)計(jì)算完并發(fā)編程之是線程安全并且高效的,在并發(fā)編程中經(jīng)常可見它的使用,在開始分析它的高并發(fā)實(shí)現(xiàn)機(jī)制前,先講講廢話,看看它是如何被引入的。電商秒殺和搶購(gòu),是兩個(gè)比較典型的互聯(lián)網(wǎng)高并發(fā)場(chǎng)景。 干貨:深度剖析分布式搜索引擎設(shè)計(jì) 分布式,高可用,和機(jī)器學(xué)習(xí)一樣,最近幾年被提及得最多的名詞,聽名字多牛逼,來(lái),我們一步一步來(lái)?yè)羝魄皟蓚€(gè)名詞,今天我們首先來(lái)說(shuō)說(shuō)分布式。 探究...

    wangdai 評(píng)論0 收藏0
  • Java多線程進(jìn)階(七)—— J.U.C之locks框架:AQS獨(dú)占功能剖析(2)

    摘要:開始獲取鎖終于輪到出場(chǎng)了,的調(diào)用過(guò)程和完全一樣,同樣拿不到鎖,然后加入到等待隊(duì)列隊(duì)尾然后,在阻塞前需要把前驅(qū)結(jié)點(diǎn)的狀態(tài)置為,以確保將來(lái)可以被喚醒至此,的執(zhí)行也暫告一段落了安心得在等待隊(duì)列中睡覺(jué)。 showImg(https://segmentfault.com/img/remote/1460000016012467); 本文首發(fā)于一世流云的專欄:https://segmentfault...

    JayChen 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<