亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

詳解Java并發(fā)包基石AQS

 更新時(shí)間:2021年06月11日 11:04:22   作者:dreamcatcher-cx  
Java并發(fā)包(JUC)中提供了很多并發(fā)工具,這其中,很多我們耳熟能詳?shù)牟l(fā)工具,譬如ReentrangLock、Semaphore,它們的實(shí)現(xiàn)都用到了一個(gè)共同的基類--AbstractQueuedSynchronizer,簡稱AQS。本文將從幾個(gè)方面來詳細(xì)介紹:基本實(shí)現(xiàn)原理、自定義同步器、源碼分析

一、概述

AQS是一個(gè)用來構(gòu)建鎖和同步器的框架,使用AQS能簡單且高效地構(gòu)造出應(yīng)用廣泛的大量的同步器,比如我們提到的ReentrantLock,Semaphore,其他的諸如ReentrantReadWriteLock,SynchronousQueue,F(xiàn)utureTask等等皆是基于AQS的。當(dāng)然,我們自己也能利用AQS非常輕松容易地構(gòu)造出符合我們自己需求的同步器。

本章我們就一起探究下這個(gè)神奇的東東,并對(duì)其實(shí)現(xiàn)原理進(jìn)行剖析理解

二、基本實(shí)現(xiàn)原理

AQS使用一個(gè)int成員變量來表示同步狀態(tài),通過內(nèi)置的FIFO隊(duì)列來完成獲取資源線程的排隊(duì)工作。

 private volatile int state;//共享變量,使用volatile修飾保證線程可見性

狀態(tài)信息通過procted類型的getState,setState,compareAndSetState進(jìn)行操作

AQS支持兩種同步方式:

1.獨(dú)占式

2.共享式

這樣方便使用者實(shí)現(xiàn)不同類型的同步組件,獨(dú)占式如ReentrantLock,共享式如Semaphore,CountDownLatch,組合式的如ReentrantReadWriteLock。總之,AQS為使用提供了底層支撐,如何組裝實(shí)現(xiàn),使用者可以自由發(fā)揮。

同步器的設(shè)計(jì)是基于模板方法模式的,一般的使用方式是這樣:

1.使用者繼承AbstractQueuedSynchronizer并重寫指定的方法。(這些重寫方法很簡單,無非是對(duì)于共享資源state的獲取和釋放)

2.將AQS組合在自定義同步組件的實(shí)現(xiàn)中,并調(diào)用其模板方法,而這些模板方法會(huì)調(diào)用使用者重寫的方法。

這其實(shí)是模板方法模式的一個(gè)很經(jīng)典的應(yīng)用。

我們來看看AQS定義的這些可重寫的方法:

  • protected boolean tryAcquire(int arg) : 獨(dú)占式獲取同步狀態(tài),試著獲取,成功返回true,反之為false
  • protected boolean tryRelease(int arg) :獨(dú)占式釋放同步狀態(tài),等待中的其他線程此時(shí)將有機(jī)會(huì)獲取到同步狀態(tài);
  • protected int tryAcquireShared(int arg) :共享式獲取同步狀態(tài),返回值大于等于0,代表獲取成功;反之獲取失?。?/li>
  • protected boolean tryReleaseShared(int arg) :共享式釋放同步狀態(tài),成功為true,失敗為false
  • protected boolean isHeldExclusively() : 是否在獨(dú)占模式下被線程占用。

關(guān)于AQS的使用,我們來簡單總結(jié)一下:

2.1、如何使用

首先,我們需要去繼承AbstractQueuedSynchronizer這個(gè)類,然后我們根據(jù)我們的需求去重寫相應(yīng)的方法,比如要實(shí)現(xiàn)一個(gè)獨(dú)占鎖,那就去重寫tryAcquire,tryRelease方法,要實(shí)現(xiàn)共享鎖,就去重寫tryAcquireShared,tryReleaseShared;最后,在我們的組件中調(diào)用AQS中的模板方法就可以了,而這些模板方法是會(huì)調(diào)用到我們之前重寫的那些方法的。也就是說,我們只需要很小的工作量就可以實(shí)現(xiàn)自己的同步組件,重寫的那些方法,僅僅是一些簡單的對(duì)于共享資源state的獲取和釋放操作,至于像是獲取資源失敗,線程需要阻塞之類的操作,自然是AQS幫我們完成了。

2.2、設(shè)計(jì)思想

對(duì)于使用者來講,我們無需關(guān)心獲取資源失敗,線程排隊(duì),線程阻塞/喚醒等一系列復(fù)雜的實(shí)現(xiàn),這些都在AQS中為我們處理好了。我們只需要負(fù)責(zé)好自己的那個(gè)環(huán)節(jié)就好,也就是獲取/釋放共享資源state的姿勢(shì)T_T。很經(jīng)典的模板方法設(shè)計(jì)模式的應(yīng)用,AQS為我們定義好頂級(jí)邏輯的骨架,并提取出公用的線程入隊(duì)列/出隊(duì)列,阻塞/喚醒等一系列復(fù)雜邏輯的實(shí)現(xiàn),將部分簡單的可由使用者決定的操作邏輯延遲到子類中去實(shí)現(xiàn)即可。

三、自定義同步器

3.1、同步器代碼實(shí)現(xiàn)

上面大概講了一些關(guān)于AQS如何使用的理論性的東西,接下來,我們就來看下實(shí)際如何使用,直接采用JDK官方文檔中的小例子來說明問題

package juc;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
public class Mutex implements java.io.Serializable {
    //靜態(tài)內(nèi)部類,繼承AQS
    private static class Sync extends AbstractQueuedSynchronizer {
        //是否處于占用狀態(tài)
        protected boolean isHeldExclusively() {
            return getState() == 1;
        }
        //當(dāng)狀態(tài)為0的時(shí)候獲取鎖,CAS操作成功,則state狀態(tài)為1,
        public boolean tryAcquire(int acquires) {
            if (compareAndSetState(0, 1)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }
        //釋放鎖,將同步狀態(tài)置為0
        protected boolean tryRelease(int releases) {
            if (getState() == 0) throw new IllegalMonitorStateException();
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }
    }
        //同步對(duì)象完成一系列復(fù)雜的操作,我們僅需指向它即可
        private final Sync sync = new Sync();
        //加鎖操作,代理到acquire(模板方法)上就行,acquire會(huì)調(diào)用我們重寫的tryAcquire方法
        public void lock() {
            sync.acquire(1);
        }
        public boolean tryLock() {
            return sync.tryAcquire(1);
        }
        //釋放鎖,代理到release(模板方法)上就行,release會(huì)調(diào)用我們重寫的tryRelease方法。
        public void unlock() {
            sync.release(1);
        }
        public boolean isLocked() {
            return sync.isHeldExclusively();
        }
}

3.2、同步器代碼測(cè)試

測(cè)試下這個(gè)自定義的同步器,我們使用之前文章中做過的并發(fā)環(huán)境下a++的例子來說明問題(a++的原子性其實(shí)最好使用原子類AtomicInteger來解決,此處用Mutex有點(diǎn)大炮打蚊子的意味,好在能說明問題就好)

package juc;

import java.util.concurrent.CyclicBarrier;

public class TestMutex {
    private static CyclicBarrier barrier = new CyclicBarrier(31);
    private static int a = 0;
    private static  Mutex mutex = new Mutex();

    public static void main(String []args) throws Exception {
        //說明:我們啟用30個(gè)線程,每個(gè)線程對(duì)i自加10000次,同步正常的話,最終結(jié)果應(yīng)為300000;
        //未加鎖前
        for(int i=0;i<30;i++){
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    for(int i=0;i<10000;i++){
                        increment1();//沒有同步措施的a++;
                    }
                    try {
                        barrier.await();//等30個(gè)線程累加完畢
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
            t.start();
        }
        barrier.await();
        System.out.println("加鎖前,a="+a);
        //加鎖后
        barrier.reset();//重置CyclicBarrier
        a=0;
        for(int i=0;i<30;i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for(int i=0;i<10000;i++){
                        increment2();//a++采用Mutex進(jìn)行同步處理
                    }
                    try {
                        barrier.await();//等30個(gè)線程累加完畢
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
        barrier.await();
        System.out.println("加鎖后,a="+a);
    }
    /**
    * 沒有同步措施的a++
    * @return
    */
    public static void increment1(){
        a++;
    }
    /**
    * 使用自定義的Mutex進(jìn)行同步處理的a++
    */
    public static void increment2(){
        mutex.lock();
        a++;
        mutex.unlock();
    }
}

測(cè)試結(jié)果:

加鎖前,a=279204加鎖后,a=300000

四、源碼分析

我們先來簡單描述下AQS的基本實(shí)現(xiàn),前面我們提到過,AQS維護(hù)一個(gè)共享資源state,通過內(nèi)置的FIFO來完成獲取資源線程的排隊(duì)工作。(這個(gè)內(nèi)置的同步隊(duì)列稱為"CLH"隊(duì)列)。該隊(duì)列由一個(gè)一個(gè)的Node結(jié)點(diǎn)組成,每個(gè)Node結(jié)點(diǎn)維護(hù)一個(gè)prev引用和next引用,分別指向自己的前驅(qū)和后繼結(jié)點(diǎn)。AQS維護(hù)兩個(gè)指針,分別指向隊(duì)列頭部head和尾部tail。

其實(shí)就是個(gè)雙端雙向鏈表。

當(dāng)線程獲取資源失?。ū热鐃ryAcquire時(shí)試圖設(shè)置state狀態(tài)失敗),會(huì)被構(gòu)造成一個(gè)結(jié)點(diǎn)加入CLH隊(duì)列中,同時(shí)當(dāng)前線程會(huì)被阻塞在隊(duì)列中(通過LockSupport.park實(shí)現(xiàn),其實(shí)是等待態(tài))。當(dāng)持有同步狀態(tài)的線程釋放同步狀態(tài)時(shí),會(huì)喚醒后繼結(jié)點(diǎn),然后此結(jié)點(diǎn)線程繼續(xù)加入到對(duì)同步狀態(tài)的爭奪中。

4.1、Node結(jié)點(diǎn)

Node結(jié)點(diǎn)是AbstractQueuedSynchronizer中的一個(gè)靜態(tài)內(nèi)部類,我們撿Node的幾個(gè)重要屬性來說一下

static final class Node {
        /** waitStatus值,表示線程已被取消(等待超時(shí)或者被中斷)*/
        static final int CANCELLED =  1;
        /** waitStatus值,表示后繼線程需要被喚醒(unpaking)*/
        static final int SIGNAL    = -1;
        /**waitStatus值,表示結(jié)點(diǎn)線程等待在condition上,當(dāng)被signal后,會(huì)從等待隊(duì)列轉(zhuǎn)移到同步到隊(duì)列中 */
        /** waitStatus value to indicate thread is waiting on condition */
        static final int CONDITION = -2;
       /** waitStatus值,表示下一次共享式同步狀態(tài)會(huì)被無條件地傳播下去
        static final int PROPAGATE = -3;
        /** 等待狀態(tài),初始為0 */
        volatile int waitStatus;
        /**當(dāng)前結(jié)點(diǎn)的前驅(qū)結(jié)點(diǎn) */
        volatile Node prev;
        /** 當(dāng)前結(jié)點(diǎn)的后繼結(jié)點(diǎn) */
        volatile Node next;
        /** 與當(dāng)前結(jié)點(diǎn)關(guān)聯(lián)的排隊(duì)中的線程 */
        volatile Thread thread;
        /** ...... */
    }

4.2、獨(dú)占式

獲取同步狀態(tài)--acquire()

來看看acquire方法,lock方法一般會(huì)直接代理到acquire上

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

我們來簡單理一下代碼邏輯:

a.首先,調(diào)用使用者重寫的tryAcquire方法,若返回true,意味著獲取同步狀態(tài)成功,后面的邏輯不再執(zhí)行;若返回false,也就是獲取同步狀態(tài)失敗,進(jìn)入b步驟;

b.此時(shí),獲取同步狀態(tài)失敗,構(gòu)造獨(dú)占式同步結(jié)點(diǎn),通過addWatiter將此結(jié)點(diǎn)添加到同步隊(duì)列的尾部(此時(shí)可能會(huì)有多個(gè)線程結(jié)點(diǎn)試圖加入同步隊(duì)列尾部,需要以線程安全的方 式添加);

c.該結(jié)點(diǎn)以在隊(duì)列中嘗試獲取同步狀態(tài),若獲取不到,則阻塞結(jié)點(diǎn)線程,直到被前驅(qū)結(jié)點(diǎn)喚醒或者被中斷。

addWaiter

為獲取同步狀態(tài)失敗的線程,構(gòu)造成一個(gè)Node結(jié)點(diǎn),添加到同步隊(duì)列尾部

private Node addWaiter(Node mode) {
    Node node = new Node(Thread.currentThread(), mode);//構(gòu)造結(jié)點(diǎn)
    //指向尾結(jié)點(diǎn)tail
    Node pred = tail;
    //如果尾結(jié)點(diǎn)不為空,CAS快速嘗試在尾部添加,若CAS設(shè)置成功,返回;否則,eng。
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    enq(node);
    return node;
}

先cas快速設(shè)置,若失敗,進(jìn)入enq方法  

將結(jié)點(diǎn)添加到同步隊(duì)列尾部這個(gè)操作,同時(shí)可能會(huì)有多個(gè)線程嘗試添加到尾部,是非線程安全的操作。

以上代碼可以看出,使用了compareAndSetTail這個(gè)cas操作保證安全添加尾結(jié)點(diǎn)。

enq方法

private Node enq(final Node node) {
    for (;;) {
        Node t = tail;
        if (t == null) { //如果隊(duì)列為空,創(chuàng)建結(jié)點(diǎn),同時(shí)被head和tail引用
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            node.prev = t;
            if (compareAndSetTail(t, node)) {//cas設(shè)置尾結(jié)點(diǎn),不成功就一直重試
                t.next = node;
                return t;
            }
        }
    }
}

enq內(nèi)部是個(gè)死循環(huán),通過CAS設(shè)置尾結(jié)點(diǎn),不成功就一直重試。很經(jīng)典的CAS自旋的用法,我們?cè)谥瓣P(guān)于原子類的源碼分析中也提到過。這是一種樂觀的并發(fā)策略。

最后,看下acquireQueued方法

acquireQueued

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {//死循環(huán)
            final Node p = node.predecessor();//找到當(dāng)前結(jié)點(diǎn)的前驅(qū)結(jié)點(diǎn)
            if (p == head && tryAcquire(arg)) {//如果前驅(qū)結(jié)點(diǎn)是頭結(jié)點(diǎn),才tryAcquire,其他結(jié)點(diǎn)是沒有機(jī)會(huì)tryAcquire的。
                setHead(node);//獲取同步狀態(tài)成功,將當(dāng)前結(jié)點(diǎn)設(shè)置為頭結(jié)點(diǎn)。
                p.next = null; // 方便GC
                failed = false;
                return interrupted;
            }
            // 如果沒有獲取到同步狀態(tài),通過shouldParkAfterFailedAcquire判斷是否應(yīng)該阻塞,parkAndCheckInterrupt用來阻塞線程
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

acquireQueued內(nèi)部也是一個(gè)死循環(huán),只有前驅(qū)結(jié)點(diǎn)是頭結(jié)點(diǎn)的結(jié)點(diǎn),也就是老二結(jié)點(diǎn),才有機(jī)會(huì)去tryAcquire;若tryAcquire成功,表示獲取同步狀態(tài)成功,將此結(jié)點(diǎn)設(shè)置為頭結(jié)點(diǎn);若是非老二結(jié)點(diǎn),或者tryAcquire失敗,則進(jìn)入shouldParkAfterFailedAcquire去判斷判斷當(dāng)前線程是否應(yīng)該阻塞,若可以,調(diào)用parkAndCheckInterrupt阻塞當(dāng)前線程,直到被中斷或者被前驅(qū)結(jié)點(diǎn)喚醒。若還不能休息,繼續(xù)循環(huán)。

shouldParkAfterFailedAcquire

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    //獲取前驅(qū)結(jié)點(diǎn)的wait值 
    int ws = pred.waitStatus;
    if (ws == Node.SIGNAL)//若前驅(qū)結(jié)點(diǎn)的狀態(tài)是SIGNAL,意味著當(dāng)前結(jié)點(diǎn)可以被安全地park
        return true;
    if (ws > 0) {
    // ws>0,只有CANCEL狀態(tài)ws才大于0。若前驅(qū)結(jié)點(diǎn)處于CANCEL狀態(tài),也就是此結(jié)點(diǎn)線程已經(jīng)無效,從后往前遍歷,找到一個(gè)非CANCEL狀態(tài)的結(jié)點(diǎn),將自己設(shè)置為它的后繼結(jié)點(diǎn)
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {  
        // 若前驅(qū)結(jié)點(diǎn)為其他狀態(tài),將其設(shè)置為SIGNAL狀態(tài)
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

若shouldParkAfterFailedAcquire返回true,也就是當(dāng)前結(jié)點(diǎn)的前驅(qū)結(jié)點(diǎn)為SIGNAL狀態(tài),則意味著當(dāng)前結(jié)點(diǎn)可以放心休息,進(jìn)入parking狀態(tài)了。parkAncCheckInterrupt阻塞線程并處理中斷。

private final boolean parkAndCheckInterrupt() {
    LockSupport.park(this);//使用LockSupport使線程進(jìn)入阻塞狀態(tài)
    return Thread.interrupted();// 線程是否被中斷過
}

至此,關(guān)于acquire的方法源碼已經(jīng)分析完畢,我們來簡單總結(jié)下

a.首先tryAcquire獲取同步狀態(tài),成功則直接返回;否則,進(jìn)入下一環(huán)節(jié);

b.線程獲取同步狀態(tài)失敗,就構(gòu)造一個(gè)結(jié)點(diǎn),加入同步隊(duì)列中,這個(gè)過程要保證線程安全;

c.加入隊(duì)列中的結(jié)點(diǎn)線程進(jìn)入自旋狀態(tài),若是老二結(jié)點(diǎn)(即前驅(qū)結(jié)點(diǎn)為頭結(jié)點(diǎn)),才有機(jī)會(huì)嘗試去獲取同步狀態(tài);否則,當(dāng)其前驅(qū)結(jié)點(diǎn)的狀態(tài)為SIGNAL,線程便可安心休息,進(jìn)入阻塞狀態(tài),直到被中斷或者被前驅(qū)結(jié)點(diǎn)喚醒。

釋放同步狀態(tài)--release()

當(dāng)前線程執(zhí)行完自己的邏輯之后,需要釋放同步狀態(tài),來看看release方法的邏輯

public final boolean release(int arg) {
    if (tryRelease(arg)) {//調(diào)用使用者重寫的tryRelease方法,若成功,喚醒其后繼結(jié)點(diǎn),失敗則返回false
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);//喚醒后繼結(jié)點(diǎn)
        return true;
    }
    return false;
}

unparkSuccessor:喚醒后繼結(jié)點(diǎn) 

private void unparkSuccessor(Node node) {
    //獲取wait狀態(tài)
    int ws = node.waitStatus;
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);// 將等待狀態(tài)waitStatus設(shè)置為初始值0
    Node s = node.next;//后繼結(jié)點(diǎn)
    if (s == null || s.waitStatus > 0) {//若后繼結(jié)點(diǎn)為空,或狀態(tài)為CANCEL(已失效),則從后尾部往前遍歷找到一個(gè)處于正常阻塞狀態(tài)的結(jié)點(diǎn)     進(jìn)行喚醒
        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);//使用LockSupprot喚醒結(jié)點(diǎn)對(duì)應(yīng)的線程
}

release的同步狀態(tài)相對(duì)簡單,需要找到頭結(jié)點(diǎn)的后繼結(jié)點(diǎn)進(jìn)行喚醒,若后繼結(jié)點(diǎn)為空或處于CANCEL狀態(tài),從后向前遍歷找尋一個(gè)正常的結(jié)點(diǎn),喚醒其對(duì)應(yīng)線程。

4.3、共享式

共享式:共享式地獲取同步狀態(tài)。對(duì)于獨(dú)占式同步組件來講,同一時(shí)刻只有一個(gè)線程能獲取到同步狀態(tài),其他線程都得去排隊(duì)等待,其待重寫的嘗試獲取同步狀態(tài)的方法tryAcquire返回值為boolean,這很容易理解;對(duì)于共享式同步組件來講,同一時(shí)刻可以有多個(gè)線程同時(shí)獲取到同步狀態(tài),這也是“共享”的意義所在。其待重寫的嘗試獲取同步狀態(tài)的方法tryAcquireShared返回值為int。

protected int tryAcquireShared(int arg) {
    throw new UnsupportedOperationException();
}

1.當(dāng)返回值大于0時(shí),表示獲取同步狀態(tài)成功,同時(shí)還有剩余同步狀態(tài)可供其他線程獲?。?/p>

2.當(dāng)返回值等于0時(shí),表示獲取同步狀態(tài)成功,但沒有可用同步狀態(tài)了;

3.當(dāng)返回值小于0時(shí),表示獲取同步狀態(tài)失敗。

獲取同步狀態(tài)--acquireShared  

public final void acquireShared(int arg) {
    if (tryAcquireShared(arg) < 0)//返回值小于0,獲取同步狀態(tài)失敗,排隊(duì)去;獲取同步狀態(tài)成功,直接返回去干自己的事兒。
        doAcquireShared(arg);
}

doAcquireShared

private void doAcquireShared(int arg) {
    final Node node = addWaiter(Node.SHARED);//構(gòu)造一個(gè)共享結(jié)點(diǎn),添加到同步隊(duì)列尾部。若隊(duì)列初始為空,先添加一個(gè)無意義的傀儡結(jié)點(diǎn),再將新節(jié)點(diǎn)添加到隊(duì)列尾部。
    boolean failed = true;//是否獲取成功
    try {
        boolean interrupted = false;//線程parking過程中是否被中斷過
        for (;;) {//死循環(huán)
            final Node p = node.predecessor();//找到前驅(qū)結(jié)點(diǎn)
            if (p == head) {//頭結(jié)點(diǎn)持有同步狀態(tài),只有前驅(qū)是頭結(jié)點(diǎn),才有機(jī)會(huì)嘗試獲取同步狀態(tài)
                int r = tryAcquireShared(arg);//嘗試獲取同步裝填
                if (r >= 0) {//r>=0,獲取成功
                    setHeadAndPropagate(node, r);//獲取成功就將當(dāng)前結(jié)點(diǎn)設(shè)置為頭結(jié)點(diǎn),若還有可用資源,傳播下去,也就是繼續(xù)喚醒后繼結(jié)點(diǎn)
                    p.next = null; // 方便GC
                    if (interrupted)
                        selfInterrupt();
                    failed = false;
                    return;
                }
            }
            if (shouldParkAfterFailedAcquire(p, node) &&//是否能安心進(jìn)入parking狀態(tài)
                parkAndCheckInterrupt())//阻塞線程
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

大體邏輯與獨(dú)占式的acquireQueued差距不大,只不過由于是共享式,會(huì)有多個(gè)線程同時(shí)獲取到線程,也可能同時(shí)釋放線程,空出很多同步狀態(tài),所以當(dāng)排隊(duì)中的老二獲取到同步狀態(tài),如果還有可用資源,會(huì)繼續(xù)傳播下去。

setHeadAndPropagate

private void setHeadAndPropagate(Node node, int propagate) {
    Node h = head; // Record old head for check below
    setHead(node);
    if (propagate > 0 || h == null || h.waitStatus < 0) {
        Node s = node.next;
        if (s == null || s.isShared())
            doReleaseShared();
    }
}

釋放同步狀態(tài)--releaseShared

public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
        doReleaseShared();//釋放同步狀態(tài)
        return true;
    }
    return false;
}

doReleaseShared

private void doReleaseShared() {
    for (;;) {//死循環(huán),共享模式,持有同步狀態(tài)的線程可能有多個(gè),采用循環(huán)CAS保證線程安全
        Node h = head;
        if (h != null && h != tail) {
            int ws = h.waitStatus;
            if (ws == Node.SIGNAL) {
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                    continue;          
                unparkSuccessor(h);//喚醒后繼結(jié)點(diǎn)
            }
            else if (ws == 0 &&
                        !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                continue;                
        }
        if (h == head)              
            break;
    }
}

代碼邏輯比較容易理解,需要注意的是,共享模式,釋放同步狀態(tài)也是多線程的,此處采用了CAS自旋來保證。

五、總結(jié)

關(guān)于AQS的介紹及源碼分析到此為止了。

AQS是JUC中很多同步組件的構(gòu)建基礎(chǔ),簡單來講,它內(nèi)部實(shí)現(xiàn)主要是狀態(tài)變量state和一個(gè)FIFO隊(duì)列來完成,同步隊(duì)列的頭結(jié)點(diǎn)是當(dāng)前獲取到同步狀態(tài)的結(jié)點(diǎn),獲取同步狀態(tài)state失敗的線程,會(huì)被構(gòu)造成一個(gè)結(jié)點(diǎn)(或共享式或獨(dú)占式)加入到同步隊(duì)列尾部(采用自旋CAS來保證此操作的線程安全),隨后線程會(huì)阻塞;釋放時(shí)喚醒頭結(jié)點(diǎn)的后繼結(jié)點(diǎn),使其加入對(duì)同步狀態(tài)的爭奪中。

AQS為我們定義好了頂層的處理實(shí)現(xiàn)邏輯,我們?cè)谑褂肁QS構(gòu)建符合我們需求的同步組件時(shí),只需重寫tryAcquire,tryAcquireShared,tryRelease,tryReleaseShared幾個(gè)方法,來決定同步狀態(tài)的釋放和獲取即可,至于背后復(fù)雜的線程排隊(duì),線程阻塞/喚醒,如何保證線程安全,都由AQS為我們完成了,這也是非常典型的模板方法的應(yīng)用。AQS定義好頂級(jí)邏輯的骨架,并提取出公用的線程入隊(duì)列/出隊(duì)列,阻塞/喚醒等一系列復(fù)雜邏輯的實(shí)現(xiàn),將部分簡單的可由使用者決定的操作邏輯延遲到子類中去實(shí)現(xiàn)。

以上就是詳解Java并發(fā)包基石AQS的詳細(xì)內(nèi)容,更多關(guān)于Java并發(fā)包基石AQS的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • tk.Mybatis 插入數(shù)據(jù)獲取Id問題

    tk.Mybatis 插入數(shù)據(jù)獲取Id問題

    本文主要介紹了tk.Mybatis 插入數(shù)據(jù)獲取Id問題,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • java實(shí)現(xiàn)隨機(jī)抽取獎(jiǎng)品工具類

    java實(shí)現(xiàn)隨機(jī)抽取獎(jiǎng)品工具類

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)隨機(jī)抽取獎(jiǎng)品工具類,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-05-05
  • java設(shè)計(jì)模式之中介者模式

    java設(shè)計(jì)模式之中介者模式

    這篇文章主要為大家詳細(xì)介紹了java設(shè)計(jì)模式之中介者模式,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-08-08
  • 使用?Spring?Boot?Admin?監(jiān)控應(yīng)用狀態(tài)的詳細(xì)過程

    使用?Spring?Boot?Admin?監(jiān)控應(yīng)用狀態(tài)的詳細(xì)過程

    這篇文章主要介紹了使用?Spring?Boot?Admin?監(jiān)控應(yīng)用狀態(tài),該模塊采集應(yīng)用的內(nèi)部信息,并暴露給外部的模塊,支持?HTTP?和?JMX,并可以與一些第三方監(jiān)控系統(tǒng)(如?Prometheus)整合,需要的朋友可以參考下
    2022-09-09
  • Spring + Spring Boot + MyBatis + MongoDB的整合教程

    Spring + Spring Boot + MyBatis + MongoDB的整合教程

    這篇文章主要給大家介紹了關(guān)于Spring + Spring Boot + MyBatis + MongoDB的整合教程,文中通過圖文以及示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧。
    2017-12-12
  • rocketMQ如何避免消息重復(fù)消費(fèi)問題

    rocketMQ如何避免消息重復(fù)消費(fèi)問題

    這篇文章主要介紹了rocketMQ如何避免消息重復(fù)消費(fèi)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-06-06
  • SpringBoot讀取properties配置文件中的數(shù)據(jù)的三種方法

    SpringBoot讀取properties配置文件中的數(shù)據(jù)的三種方法

    本文主要介紹了SpringBoot讀取properties配置文件中的數(shù)據(jù)的三種方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-06-06
  • SpringBoot監(jiān)控SQL運(yùn)行情況的流程步驟

    SpringBoot監(jiān)控SQL運(yùn)行情況的流程步驟

    Druid是Java語言中最好的數(shù)據(jù)庫連接池,雖然?HikariCP?的速度稍快,但是,Druid能夠提供強(qiáng)大的監(jiān)控和擴(kuò)展功能?,也是阿里巴巴的開源項(xiàng)目,本文給大家介紹了SpringBoot監(jiān)控SQL運(yùn)行情況的流程步驟,需要的朋友可以參考下
    2024-03-03
  • Spring整合Kaptcha谷歌驗(yàn)證碼工具的開發(fā)步驟

    Spring整合Kaptcha谷歌驗(yàn)證碼工具的開發(fā)步驟

    這篇文章主要介紹了Spring整合Kaptcha谷歌驗(yàn)證碼工具的開發(fā)步驟,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-01-01
  • 詳解5種Java中常見限流算法

    詳解5種Java中常見限流算法

    在高并發(fā)系統(tǒng)中,出于系統(tǒng)保護(hù)角度考慮,通常會(huì)對(duì)流量進(jìn)行限流;不但在工作中要頻繁使用,而且也是面試中的高頻考點(diǎn)。本文就為大家整理了5種Java中常見限流算法,需要的可以參考一下
    2023-04-04

最新評(píng)論