精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

三分鐘帶你搞懂 AQS 原理設計

開發 前端
本文從ReentrantLock?源碼分析到AQS?原理解析,進行了一次知識內容的總結,從上文的分析中可以看出,AQS是JUC包下線程同步器實現的基石。

一、摘要

在之前的文章中,我們介紹了 ReentrantLock、ReadWriteLock、CountDownLatch、CyclicBarrier、Semaphore、ThreadPoolExecutor 等并發工具類的使用方式,它們在請求共享資源的時候,都能實現線程同步的效果。

在使用方式上稍有不同,有的是獨占式,多個線程競爭時只有一個線程能執行方法,比如 ReentrantLock 等;有的是共享式,多個線程可以同時執行方法,比如:ReadWriteLock、CountDownLatch、Semaphore 等,不同的實現爭用共享資源的方式也不同。

如果仔細閱讀源碼,會發現它們都是基于AbstractQueuedSynchronizer這個抽象類實現的,我們簡稱 AQS。

AQS 是一個提供了原子式管理同步狀態、阻塞和喚醒線程功能的框架,是除了 Java 自帶的synchronized關鍵字之外的鎖實現機制。

可以這么說,AQS是JUC包下線程同步類的基石,也是很多面試官喜歡提問的話題,掌握AQS原理對我們深入理解線程同步技術有著非常重要的意義。

本文以ReentrantLock作為切入點,來解讀AQS相關的知識點,最后配上簡單的應用示例來幫助大家理解 AQS,如果有描述不對的地方,歡迎大家留言指出,不勝感激!

二、ReentrantLock

在之前的線程系列文章中,我們介紹了ReentrantLock的基本用法,它是一個可重入的互斥鎖,它具有與使用synchronized關鍵字一樣的效果,并且功能更加強大,編程更加靈活,支持公平鎖和非公平鎖兩種模式。

使用方式也非常簡單,只需要在相應的代碼上調用加鎖和釋放鎖方法即可,簡單示例如下!

public class Counter {

    // 默認非公平鎖模式
    private final Lock lock = new ReentrantLock();

    public void add() {
        // 加鎖
        lock.lock();
        try {
            // 具體業務邏輯...
        } finally {
            // 釋放鎖
            lock.unlock();
        }
    }
}

如果閱讀lock()和unlock()方法,會發現它的底層都是由AQS來實現的。

下面,我們一起來看看這兩個方法的源碼實現,本文源碼內容摘取自 JDK 1.8 版本,可能不同的版本略有區別!

2.1、lock 方法源碼

public class ReentrantLock implements Lock, java.io.Serializable {
    
    // 同步鎖實現類
    private final Sync sync;

    public ReentrantLock() {
        // 默認構造方法為非公平鎖實現類
        sync = new NonfairSync();
    }

    public ReentrantLock(boolean fair) {
        // true:公平鎖實現類,false:非公平鎖實現類
        sync = fair ? new FairSync() : new NonfairSync();
    }

    public void lock() {
        // 加鎖操作
        sync.lock();
    }

    // 非公平鎖實現類
    static final class NonfairSync extends Sync {

         // 加鎖操作
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }
    }

    // 公平鎖實現類
    static final class FairSync extends Sync {

        // 加鎖操作
        final void lock() {
            acquire(1);
        }
    }

    // 公平鎖和非公平鎖,都繼承自 AQS
    abstract static class Sync extends AbstractQueuedSynchronizer {

        // lock 抽象方法
        abstract void lock();
    }
}

從源碼上可以清晰的看到,當初始化ReentrantLock對象時,需要指定鎖的模式。

默認構造方法是非公平鎖模式,采用的是NonfairSync內部實現類;公平鎖模式下,則采用的是FairSync內部實現類;這兩個內部實現類都繼承了Sync抽象類;同時,Sync也繼承了AbstractQueuedSynchronizer,也就是我們上文提到的AQS。

如果把lock()方法的請求鏈路進行抽象,可以用如下圖進行簡要概括。

圖片圖片

無論是非公平鎖模式還是公平鎖模式,可能最終都會調用AQS的acquire()方法,它表示通過獨占式的方式加鎖,我們繼續往下看這個方法的源碼,部分核心代碼如下:

public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {

    // 通過獨占式的方式加鎖
    public final void acquire(int arg) {
        // 嘗試加鎖,會回調具體的實現類
        if (!tryAcquire(arg) &&
            // 如果嘗試加鎖失敗,將當前線程加入等待隊列
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

    // 由子類完成加鎖邏輯的實現,支持重寫該方法
    protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }
}

從AQS的源碼上可以看出,acquire()方法并不進行具體加鎖邏輯的實現,而是通過具體的實現類重寫tryAcquire()方法來完成加鎖操作,如果加鎖失敗,會將當前線程加入等待隊列。

如果是非公平鎖模式,會回調ReentrantLock類的NonfairSync.tryAcquire()方法;如果是公平鎖模式,會回調ReentrantLock類的FairSync.tryAcquire()方法,我們繼續回看ReentrantLock類的源碼。

非公平鎖NonfairSync靜態內部實現類,相關的源碼如下!

// 非公平鎖實現類
static final class NonfairSync extends Sync {

     // 加鎖操作
    final void lock() {
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }

    // 嘗試非公平方式加鎖,重寫父類 tryAcquire 方法
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            // 采用CAS方式修改線程同步狀態,如果成功返回true
            if (compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        else if (current == getExclusiveOwnerThread()) {
            // 支持當前線程,重復獲得鎖,將state值加1
            int nextc = c + acquires;
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }

}

公平鎖FairSync靜態內部實現類,相關的源碼如下!

// 公平鎖實現類
static final class FairSync extends Sync {

    // 加鎖操作
    final void lock() {
        acquire(1);
    }

    // 嘗試公平方式加鎖,重寫父類 tryAcquire 方法
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            // 1)判斷等待隊列是否有線程處于等待狀態,如果沒有,嘗試獲取鎖;如果有,就進入等待隊列
            // 2)采用CAS方式修改線程同步狀態,如果成功返回true
            if (!hasQueuedPredecessors() &&
                compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        else if (current == getExclusiveOwnerThread()) {
            // 支持當前線程,重復獲得鎖,將state值加1
            int nextc = c + acquires;
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }
}

從源碼上可以清晰的看到,無論是是公平鎖還是非公平鎖模式,都是采用compareAndSetState()方法(簡稱CAS)進行加鎖,如果成功就返回true;同時支持當前線程重復獲得鎖,也就是之前提到的鎖可重入機制。

唯一的區別在于:公平鎖實現類多了一個hasQueuedPredecessors()方法判斷,它的用途是判斷等待隊列是否有線程處于等待狀態,如果沒有,嘗試獲取鎖;如果有,就將當前線程存入等待隊列,依此排隊,從而保證線程通過公平方式獲取鎖的目的。

關于 CAS 實現原理,在之前的并發原子類文章中已經有所介紹,通過它加上volatile修飾符可以實現一個無鎖的線程安全訪問操作,本文不再重復解讀,有興趣的朋友可以翻閱之前的文章。

2.2、unlock 方法源碼

public class ReentrantLock implements Lock, java.io.Serializable {

    // 同步鎖實現類
    private final Sync sync;

    public void unlock() {
        // 釋放鎖操作
        sync.release(1);
    }
}

unlock()方法的釋放鎖實現相對來說就簡單多了,整個請求鏈路可以用如下圖進行簡要概括。

當調用unlock()方法時,會直接跳轉到AQS的release()方法上,AQS相關的源碼如下!

public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {

    // 釋放鎖操作
    public final boolean release(int arg) {
        // 嘗試釋放鎖
        if (tryRelease(arg)) {
            // 從隊列頭部中獲取一個等待線程,并進行喚醒操作
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

    // 由子類完成釋放鎖邏輯的實現,支持重寫該方法
    protected boolean tryRelease(int arg) {
        throw new UnsupportedOperationException();
    }
}

與加鎖操作類似,AQS的release()方法并不進行具體釋放鎖邏輯的實現,而是通過具體的實現類重寫tryRelease()方法來完成釋放鎖操作,如果釋放鎖成功,會從隊列頭部中獲取一個等待線程,并進行喚醒操作。

我們繼續回看ReentrantLock類的Sync.tryRelease()釋放鎖方法,部分核心源碼如下:

abstract static class Sync extends AbstractQueuedSynchronizer {

    // 嘗試釋放鎖
    protected final boolean tryRelease(int releases) {
        // 將state值進行減1操作
        int c = getState() - releases;
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
        boolean free = false;
        if (c == 0) {
            free = true;
            setExclusiveOwnerThread(null);
        }
        setState(c);
        return free;
    }
}

相比加鎖過程,釋放鎖要簡單的多,主要是將線程的同步狀態值進行自減操作。

三、AQS 原理淺析

如果仔細的研究 AQS 的源碼,盡管實現上很復雜,但是也有規律可循。

從上到下,整個框架可以分為五層,架構可以用如下圖來描述?。▓D片來自ReentrantLock 的實現看 AQS 的原理及應用 - 美團技術團隊)

圖片圖片

當有自定義線程同步器接入AQS時,只需要按需重寫第一層的方法即可,不需要關心底層的實現。

以加鎖為例,當調用AQS的 API 層獲取鎖方法時,會先嘗試進行加鎖操作(具體邏輯由實現類完成),如果加鎖失敗,會進入等待隊列處理環節,這些處理邏輯同時也依賴最底層的基礎數據提供層來完成。

3.1、原理概述

整個AQS實現線程同步的核心思想,可以用如下這段話來描述!

AQS 內部維護一個共享資源變量和線程等待隊列,如果被請求的共享資源空閑,那么就將當前請求資源的線程設置為有效的工作線程,將共享資源設置為鎖定狀態;如果共享資源被占用,就需要一定的阻塞等待喚醒機制來保證鎖分配。這個機制主要用的是 CLH 隊列的變體實現的,將暫時獲取不到鎖的線程加入到等待隊列中,待條件允許的時候將線程從隊列中取出并進行喚醒。

CLH 隊列是一個單向鏈表隊列,對應的還有 CLH 鎖實現,它是一個基于邏輯隊列非線程饑餓的一種自旋公平鎖實現,由 Craig、Landin 和 Hagersten 三位大佬發明,因此命名為 CLH 鎖。關于這方面的技術知識講解可以參閱這篇文章:多圖詳解 CLH 鎖的原理與實現。

而AQS中的隊列采用的是 CLH 變體的虛擬雙向隊列,通過將每一條請求共享資源的線程封裝成一個 CLH 隊列的一個節點來實現鎖的分配。

具體實現原理,可以用如下圖來簡單概括:

圖片圖片

同時,AQS中維護了一個共享資源變量state,通過它來實現線程的同步狀態控制,這個字段使用了volatile關鍵字修飾符來保證多線程下的可見性。

當多個線程嘗試獲取鎖時,會通過CAS方式來修改state值,當state=1時表示當前對象鎖已經被占有(相對獨占模式來說),此時其他線程來加鎖時會失敗,加鎖失敗的線程會被放入上文說到的FIFO等待隊列中,并且線程會被掛起,等待其他獲取鎖的線程釋放鎖才能夠被喚醒。

總結下來,用大白話說就是,AQS是基于 CLH 隊列,使用volatile修飾共享變量state,線程通過CAS方式去改變state狀態值,如果成功則獲取鎖成功,失敗則進入等待隊列,等待被喚醒的線程同步器框架。

打開 ReentrantLock、ReadWriteLock、CountDownLatch、CyclicBarrier、Semaphore 等類的源碼實現,你會發現它們的線程同步狀態都是基于AQS實現的,可以看成是AQS的衍生物。

下面我們一起來看看相關的源碼實現!

3.2、源碼淺析

3.2.1、線程同步狀態控制

AQS源碼中維護的共享資源變量state,表示同步狀態的意思,它是實現線程同步控制的關鍵字段,核心源碼如下:

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

針對state字段值的獲取和修改,AQS提供了三個方法,并且都采用Final修飾,意味著子類無法重寫它們,相關方法如下:

方法

描述

protected final int getState()

獲取state的值

protected final void setState(int newState)

設置state的值

protected final boolean compareAndSetState(int expect, int update)

使用 CAS 方式更新state

如果仔細分析源碼,state字段還有一個很大的用處,通過它可以實現多線程的獨占模式和共享模式。

以ReentrantLock和Semaphore類為例,它們的加鎖過程中state值的變化情況如下。

3.2.1.1、ReentrantLock 獨占模式的獲取鎖,簡易流程圖如下:

圖片圖片

ReentrantLock類部分核心源碼,實現邏輯如下:

public class ReentrantLock implements Lock, java.io.Serializable {

    // 非公平鎖實現類
    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        // 加鎖操作
        final void lock() {
            // 將state從0設置為1,如果成功,直接獲取當前共享資源
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                // 嘗試加鎖,會轉調tryAcquire方法
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            // 判斷state是否等于0
            if (c == 0) {
                // 嘗試state從0設置為1,如果成功,返回true
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                // 支持當前線程可重入,每調用一次,state的值加1
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }
}
3.2.1.2、Semaphore 共享模式的獲取鎖,簡易流程圖如下:

圖片圖片

Semaphore類部分核心源碼,實現邏輯如下:

public class Semaphore implements java.io.Serializable {

    // 初始化的時候,設置線程最大并發數,本質設置的是state的值
    public Semaphore(int permits) {
        sync = new NonfairSync(permits);
    }

    // 非公平鎖內部實現類
    static final class NonfairSync extends Sync {

        NonfairSync(int permits) {
            // 設置state的值
            setState(permits);
        }

        // 通過共享方式,嘗試獲取鎖
        protected int tryAcquireShared(int acquires) {
            return nonfairTryAcquireShared(acquires);
        }
    }

    // 嘗試獲取共享資源,會調用Sync.nonfairTryAcquireShared方法
    public boolean tryAcquire() {
        // 如果state的值小于0,表示無可用共享資源
        return sync.nonfairTryAcquireShared(1) >= 0;
    }

    // 抽象同步類
    abstract static class Sync extends AbstractQueuedSynchronizer {

        // 通過共享方式,嘗試獲取鎖
        final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                // 通過cas方式,設置state自減
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }
    }
}
3.2.2、公平鎖和非公平鎖實現

在上文的ReentrantLock源碼分析過程中,對于公平鎖和非公平鎖實現,其實已經有所解讀。

在AQS中所有的加鎖邏輯是有具有的實現類來完成,以ReentrantLock類為例,它的加鎖邏輯由兩個實現類來完成,分別是非公平鎖靜態內部實現類NonfairSync和公平鎖靜態內部實現類FairSync。

如上文的源碼介紹,這兩個類的的加鎖邏輯基本一致,唯一的區別在于:公平鎖實現類加鎖時,增加了一個hasQueuedPredecessors()方法判斷,這個方法會判斷等待隊列是否有線程處于等待狀態,如果沒有,嘗試獲取鎖;如果有,就進入等待隊列。

簡單的說就是,非公平鎖實現類的加鎖方式,如果有線程嘗試獲取鎖,直接嘗試通過CAS方式進行搶鎖,如果搶成功了,就直接獲取鎖,沒有搶成功就進入等待隊列;而公平鎖實現類的加鎖方式,會判斷等待隊列是否有線程處于等待狀態,如果有則不去搶鎖,乖乖排到后面,如果沒有則嘗試搶鎖。

相對來說,非公平鎖會有更好的性能,因為它的吞吐量比較大。其次,非公平鎖讓獲取鎖的時間變得更加不確定,可能會導致在阻塞隊列中的線程長期處于饑餓狀態。

Semaphore類的公平鎖和非公平鎖實現也類似,擁有兩個靜態內部實現類,源碼就不再解讀了,有興趣的朋友可以自行閱讀。

3.2.3、主要模板方法

從ReentrantLock的源碼實現中可以看出,AQS使用了模板方法設計模式,它不提供加鎖和釋放鎖的具體邏輯實現,而是由實現類重寫對應的方法來完成,這樣的好處就是實現更加的靈活,不同的線程同步器可以自行繼承AQS類,然后實現獨屬于自身的加鎖和解鎖功能。

常用的模板方法主要有以下幾個:

方法

描述

protected boolean isHeldExclusively()

判斷該線程是否正在獨占資源。只有用到Condition才需要去實現它

protected boolean tryAcquire(int arg)

獨占方式。嘗試獲取資源,arg為獲取鎖的次數,成功則返回true,失敗則返回false

protected boolean tryRelease(int arg)

獨占方式。嘗試釋放資源,arg為釋放鎖的次數,成功則返回true,失敗則返回false

protected int tryAcquireShared(int arg)

共享方式。嘗試獲取資源,arg為獲取鎖的次數,負數表示失敗;0表示成功,但沒有剩余可用資源;正數表示成功,且有剩余資源

protected boolean tryReleaseShared(int arg)

共享方式。嘗試釋放資源,arg為釋放鎖的次數,如果釋放后允許喚醒后續等待結點返回true,否則返回false

通常自定義線程同步器,要么是獨占模式,要么是共享模式。

如果是獨占模式,重寫tryAcquire()和tryRelease()方法即可,比如 ReentrantLock 類。

如果是共享模式,重寫tryAcquireShared()和tryReleaseShared()方法即可,比如 Semaphore 類。

3.2.4、線程加入等待隊列實現

當線程調用tryAcquire()方法獲取鎖失敗之后,就會調用addWaiter()方法,將當前線程加入到等待隊列中去。

addWaiter()方法,部分核心源碼如下:

public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {

    // 將當前線程加入等待隊列
    private Node addWaiter(Node mode) {
        // 以當前線程構造一個節點,嘗試通過CAS方式插入到雙向鏈表的隊尾
        Node node = new Node(Thread.currentThread(), mode);
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        // 如果插入沒有成功,則通過enq入隊
        enq(node);
        return node;
    }

    // 通過enq入隊
    private Node enq(final Node node) {
        // CAS 自旋方式,直到成功加入隊尾
        for (;;) {
            Node t = tail;
            if (t == null) { 
                // 隊列為空,創建一個空結點作為head結點,并將tail也指向它
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }
}

我們再來看看Node類節點相關的屬性,部分核心源碼如下:

static final class Node {

    // 當前節點在隊列中的狀態,狀態值枚舉含義如下:
    // 0:節點初始化時的狀態
    // 1: 表示節點引用線程由于等待超時或被打斷時的狀態
    // -1: 表示當隊列中加入后繼節點被掛起時,其前驅節點會被設置為SIGNAL狀態,表示該節點需要被喚醒
    // -2:當節點線程進入condition隊列時的狀態
    // -3:僅在釋放共享鎖releaseShared時對頭節點使用
    volatile int waitStatus;

    // 前驅節點
    volatile Node prev;

    // 后繼節點
    volatile Node next;

    //該節點的線程實例
    volatile Thread thread;

    // 指向下一個處于Condition狀態的節點(用于條件隊列)
    Node nextWaiter;

    //...
}

可以很清晰的看到,每個關鍵屬性變量都加了volatile修飾符,確保多線程環境下可見。

正如上文所介紹的,Node其實是一個雙向鏈表數據結構,大致的數據結構圖如下?。▓D片來自ReentrantLock 的實現看 AQS 的原理及應用 - 美團技術團隊)

圖片圖片

其中第一個節點,也叫頭節點,為虛節點,并不存儲任何線程信息,只是占位用;真正有數據的是從第二個節點開始,當有線程需要加入等待隊列時,會向隊尾進行插入。

線程加入等待隊列之后,會再次調用acquireQueued()方法,嘗試進行獲取鎖,如果成功或者中斷就退出,部分核心源碼如下:

final boolean acquireQueued(final Node node, int arg) {
    // 標記是否成功拿到鎖
    boolean failed = true;
    try {
        // 標記等待過程中是否中斷過
        boolean interrupted = false;
        // 開始自旋,要么獲取鎖,要么中斷
        for (;;) {
            // 獲取當前節點的前驅節點
            final Node p = node.predecessor();
            // 如果p是頭結點,說明當前節點在等待隊列的頭部,嘗試獲取鎖(頭結點是虛節點)
            if (p == head && tryAcquire(arg)) {
                // 獲取鎖成功,頭指針移動到當前node
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            // 如果p不是頭節點或者是頭節點但獲取鎖失敗,判斷當前節點是否要進入阻塞,如果滿足要求,就通過park讓線程進入阻塞狀態,等待被喚醒
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            // 如果沒有成功獲取鎖(比如超時或者被中斷),那么取消節點在隊列中的等待
            cancelAcquire(node);
    }
}

線程加入等待隊列實現,總結下來,大致步驟如下:

  • 1.調用addWaiter()方法,將當前線程封裝成一個節點,嘗試通過CAS方式插入到雙向鏈表的隊尾,如果沒有成功,再通過自旋方式插入,直到成功為止
  • 2.調用acquireQueued()方法,對在等待隊列中排隊的線程,嘗試獲取鎖操作,如果失敗,判斷當前節點是否要進入阻塞,如果滿足要求,就通過 LockSupport.park()方法讓線程進入阻塞狀態,并檢查是否被中斷,如果沒有,等待被喚醒
3.2.5、線程從等待隊列中被喚醒實現

當線程調用tryRelease()方法釋放鎖成功之后,會從等待隊列的頭部開始,獲取排隊的線程,并進行喚醒操作。

釋放鎖方法,部分核心源碼如下:

public final boolean release(int arg) {
    // 嘗試釋放鎖
    if (tryRelease(arg)) {
        // 獲取頭部節點
        Node h = head;
        if (h != null && h.waitStatus != 0)
            // 嘗試喚醒頭部節點的下一個節點中的線程
            unparkSuccessor(h);
        return true;
    }
    return false;
}

其中unparkSuccessor()是執行喚醒線程的核心方法,部分核心源碼如下:

private void unparkSuccessor(Node node) {
    // 獲取頭結點 waitStatus
    int ws = node.waitStatus;
    // 置零當前線程所在的結點狀態,允許失敗
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);

    // 獲取當前節點的下一個節點s
    Node s = node.next;
    // 如果下個節點是null或者被取消,就從隊列尾部依此尋找節點
    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);
}

線程從等待隊列中被喚醒實現,總結下來,大致步驟如下:

  • 1.當線程調用tryRelease()方法釋放鎖成功之后,會從等待隊列獲取排隊的線程
  • 2.如果隊列的頭節點的下一個節點有效,會嘗試進行喚醒節點中的線程;如果為空或者被取消,就從隊列尾部依此尋找節點,找到隊列中排在最前的有效節點,并嘗試進行喚醒操作

四、簡單應用

了解完AQS基本原理之后,按照以上的知識點,我們可以自己實現一個不可重入的互斥鎖線程同步類。

示例代碼如下:

public class MutexLock {

    // 自定義同步器
    private static class Sync extends AbstractQueuedSynchronizer {

        // 判斷是否鎖定狀態
        @Override
        protected boolean isHeldExclusively() {
            return getState() == 1;
        }

        // 嘗試獲取資源,立即返回。成功則返回true,否則false。
        @Override
        protected boolean tryAcquire(int acquires) {
            //state為0才設置為1,不支持重入!
            if (compareAndSetState(0, 1)) {
                //設置為當前線程獨占資源
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        // 嘗試釋放資源,立即返回。成功則為true,否則false。
        @Override
        protected boolean tryRelease(int releases) {
            // 判斷資源是否已被釋放
            if (getState() == 0)
                throw new IllegalMonitorStateException();
            setExclusiveOwnerThread(null);
            //釋放資源,放棄占有狀態
            setState(0);
            return true;
        }
    }

    // 真正同步類的實現都依賴繼承于AQS的自定義同步器!
    private final Sync sync = new Sync();

    // 獲取鎖,會阻塞等待,直到成功才返回
    public void lock() {
        sync.acquire(1);
    }

    // 釋放鎖
    public void unlock() {
        sync.release(1);
    }
}

測試類如下:

public class MutexLockTest {

    private static int count =0;

    private static MutexLock lock = new MutexLock();

    public static void main(String[] args) throws Exception {
        final int threadNum = 10;
        CountDownLatch latch = new CountDownLatch(threadNum);
        // 創建10個線程,同時對count進行1000相加操作
        for (int i = 0; i < threadNum; i++) {
            new Thread(new Runnable() {

                @Override
                public void run() {
                    // 加鎖
                    lock.lock();
                    for (int j = 0; j < 1000; j++) {
                        count++;
                    }
                    // 釋放鎖
                    lock.unlock();
                    // 線程數減 1
                    latch.countDown();
                }
            }).start();
        }

        // 等待線程執行完畢
        latch.await();
        System.out.println("執行結果:" +  count);
    }
}

輸出結果:

執行結果:10000

從日志輸出結果可以清晰的看到,執行結果與預期值一致!

五、小結

本文從ReentrantLock源碼分析到AQS原理解析,進行了一次知識內容的總結,從上文的分析中可以看出,AQS是JUC包下線程同步器實現的基石。

ReentrantLock、ReadWriteLock、CountDownLatch、CyclicBarrier、Semaphore、ThreadPoolExecutor 等并發工具類,線程同步的實現都基于AQS來完成,掌握AQS原理對線程同步的理解和使用至關重要。

AQS原理是面試時熱點話題,希望本篇能幫助到大家!

六、參考

1.https://www.cnblogs.com/waterystone/p/4920797.html

2.https://tech.meituan.com/2019/12/05/aqs-theory-and-apply.html

3.https://zhuanlan.zhihu.com/p/197840259

4.https://juejin.cn/post/7006895386103119908

責任編輯:武曉燕 來源: Java極客技術
相關推薦

2024-01-16 07:46:14

FutureTask接口用法

2024-07-05 09:31:37

2021-04-20 13:59:37

云計算

2022-02-17 09:24:11

TypeScript編程語言javaScrip

2024-08-30 08:50:00

2020-06-30 10:45:28

Web開發工具

2023-12-04 18:13:03

GPU編程

2025-02-24 10:40:55

2021-02-03 14:31:53

人工智能人臉識別

2024-09-13 08:49:45

2017-01-18 15:38:20

語言

2020-03-08 16:45:58

數據挖掘學習數據量

2025-10-27 01:35:00

2023-01-31 08:24:55

HashMap死循環

2024-02-22 07:37:37

對象JVM內存

2024-06-06 08:50:43

2023-12-23 18:04:40

服務Eureka工具

2025-11-17 01:11:00

Callback異步編程函數

2024-05-16 11:13:16

Helm工具release

2024-12-18 10:24:59

代理技術JDK動態代理
點贊
收藏

51CTO技術棧公眾號

欧美天天综合网| xf在线a精品一区二区视频网站| 日韩在线视频观看正片免费网站| 爱豆国产剧免费观看大全剧苏畅| 国产一区久久精品| 成人一级黄色片| 国产福利精品av综合导导航| 天海翼在线视频| 欧美电影免费网站| 欧美日韩免费一区二区三区视频| 日韩精品久久一区二区| 精品久久av| 国产成人精品一区二| 国产aaa精品| 久久久综合久久久| 精品久久久久久久| 精品捆绑美女sm三区| 在线观看高清免费视频| 暖暖在线中文免费日本| 亚洲国产激情av| 国内外成人免费视频| 91好色先生tv| 老司机精品导航| 色综合久久久888| 少妇一级黄色片| 亚洲另类春色校园小说| 91精品国产综合久久福利| 日韩av一二三四| 日韩理论视频| 一区二区三区产品免费精品久久75| 天天好比中文综合网| 乱色精品无码一区二区国产盗| 日韩福利视频导航| 久久久噜噜噜久久久| 希岛爱理中文字幕| 日韩中文在线电影| 亚洲最新av在线| japanese在线观看| eeuss国产一区二区三区四区| 欧美精品久久久久久久多人混战| 国产精品69页| 中文日产幕无线码一区二区| 亚洲第一成人在线| 996这里只有精品| 18视频在线观看网站| 国产精品精品国产色婷婷| 天堂精品一区二区三区| 岛国视频免费在线观看| 久久嫩草精品久久久精品| 国内精品视频免费| 丝袜+亚洲+另类+欧美+变态| 成人精品一区二区三区中文字幕| 国产传媒一区| 欧美一级做性受免费大片免费| 国产精品99久久久久久久女警| 国产日韩欧美电影在线观看| 国产一区二区在线视频聊天| 久久精品免费观看| 成人a在线视频| 国产视频aaa| 国产精品亚洲一区二区三区妖精| 亚洲japanese制服美女| 国产xxxx孕妇| 国产91丝袜在线播放0| 国产一区二区三区黄| 天天干天天干天天干| 97国产精品videossex| 久久久综合香蕉尹人综合网 | 日韩精品在线观看视频| 亚洲精品在线视频免费观看 | 一区二区精品视频| 毛片在线播放a| 亚洲精品免费电影| 免费不卡av在线| 在线天堂资源www在线污| 91国产免费看| 国产又粗又长又爽又黄的视频| 蜜桃在线一区| 精品丝袜一区二区三区| 亚洲女优在线观看| 影音先锋成人在线电影| 97国产精品人人爽人人做| 国产91精品看黄网站在线观看| 美腿丝袜亚洲色图| 99久热re在线精品996热视频| 日本激情一区二区| 欧美激情资源网| 永久免费网站视频在线观看| а√天堂中文资源在线bt| 91成人看片片| 久久久男人的天堂| 欧美精品一区二区三区中文字幕| 久久精视频免费在线久久完整在线看| 青娱乐国产精品| 免费国产自线拍一欧美视频| 成人国内精品久久久久一区| 黄色aaa大片| 久久久www成人免费无遮挡大片| 亚洲午夜精品一区二区| 精品一性一色一乱农村| 在线观看视频一区| 黄页网站在线看| 红桃成人av在线播放| 久久天天躁狠狠躁夜夜躁| 精品成人免费视频| 久草在线在线精品观看| 久久66热这里只有精品| 日韩在线资源| 精品久久中文字幕| 亚洲精品在线网址| 国产欧美日韩| 性亚洲最疯狂xxxx高清| 国产精品毛片一区二区在线看舒淇| 成人动漫在线一区| 中文字幕一区二区三区四区五区六区 | 久久在线观看| 夜夜嗨av色一区二区不卡| 久久久久久久蜜桃| 久久99精品久久久久婷婷| 久久er99热精品一区二区三区 | 日韩美女视频19| 欧美一级片中文字幕| 国产精品网在线观看| 久久精品国产96久久久香蕉| 婷婷激情五月综合| 9l国产精品久久久久麻豆| 视色,视色影院,视色影库,视色网| 天堂久久午夜av| 日韩电影中文字幕一区| 久久久精品99| 国内精品伊人久久久久av影院 | 国产综合色产| 国产在线观看不卡| 国产精品久久久久一区二区国产 | 欧美日韩国产成人在线| 91久久国语露脸精品国产高跟| 久久久久久久久久久久久女国产乱| 午夜电影网亚洲视频| 国产精品裸体瑜伽视频| 日韩成人精品| 久久国产精品久久精品| 91av国产精品| 中文字幕日韩精品一区| 日韩av片网站| 欧美一区2区| 国产精品十八以下禁看| 成人高清免费在线播放| 欧美综合天天夜夜久久| 欧美大波大乳巨大乳| 久久亚洲风情| 日韩一区二区电影在线观看| 九九热线视频只有这里最精品| 日韩精品在线视频观看| 五月天婷婷激情| 久久综合久久综合久久| 国产一区亚洲二区三区| 欧美人与拘性视交免费看| 欧美最近摘花xxxx摘花| 欧美日韩视频精品二区| 欧美性xxxx极品hd欧美风情| 91中文字幕永久在线| 丝袜美腿亚洲色图| 亚洲欧洲精品在线| 白嫩亚洲一区二区三区| 欧美大成色www永久网站婷| 国产黄色一级大片| 婷婷亚洲久悠悠色悠在线播放| 久久久久久久人妻无码中文字幕爆| 亚洲国产日韩在线| 久久国产精品久久精品国产| 快播电影网址老女人久久| 中文字幕亚洲精品| av男人天堂网| 亚洲 欧美综合在线网络| 国产精品815.cc红桃| 免费成人美女在线观看| 国产专区在线视频| 精品精品精品| 国产精品久久久久久久久久久久久久| 秋霞成人影院| 精品福利二区三区| 黄色一级视频免费看| 日韩成人av电影| 日韩欧美美女一区二区三区| 国产精品99精品| 国产亚洲欧美一区在线观看| 在线观看中文av| 99riav1国产精品视频| 日韩一区二区三区资源| 日本一区二区乱| 97欧美精品一区二区三区| www.成人.com| 日韩免费一区二区三区在线播放| 最新中文字幕一区| 中文字幕一区在线观看视频| 亚洲欧美日韩偷拍| 热久久国产精品| 欧美一级免费播放| 成人在线免费视频观看| 官网99热精品| 日韩电影精品| 欧美亚洲日本网站| www.欧美日本韩国| 亚洲天堂视频在线观看| 丰满人妻熟女aⅴ一区| 在线观看av一区| av资源吧首页| ...av二区三区久久精品| 日韩免费高清一区二区| 精品影视av免费| 国产男女免费视频| 永久91嫩草亚洲精品人人| 欧美日韩大片一区二区三区| 日韩在线网址| 国产日韩换脸av一区在线观看| 欧美办公室脚交xxxx| 不卡av电影院| 成人免费在线电影| 亚洲精品wwwww| 国产视频在线一区| 欧美色视频一区| 五月天激情国产综合婷婷婷| 一区二区三区免费在线观看| 国产精品视频在| 久久噜噜亚洲综合| 艳妇乳肉豪妇荡乳xxx| 国产剧情一区二区三区| 日本免费色视频| 日韩影院精彩在线| 国模无码视频一区二区三区| 欧美日韩91| 97精品国产97久久久久久粉红| 狠狠色丁香婷婷综合影院| 久久久久一区二区| 亚洲素人在线| 久久一区二区精品| 牛牛视频精品一区二区不卡| 成人av中文| 亚洲精品18| 亚洲综合第一页| 国产精品一区三区在线观看| 国产日韩亚洲欧美| 精品国产美女a久久9999| 国产精品国模在线| 少妇精品视频一区二区免费看| 日本欧美在线视频| 亚洲美女久久精品| 青青a在线精品免费观看| 欧美a级在线观看| 欧美在线www| 欧美大片免费| 日本伊人精品一区二区三区介绍| 这里有精品可以观看| 日本欧美中文字幕| 一区二区视频免费完整版观看| 人人澡人人澡人人看欧美| 男人最爱成人网| 国产精品高潮呻吟久久av无限| 国产综合av| 国产日韩亚洲欧美| 久久一级大片| 狠狠色噜噜狠狠色综合久| 国产精品玖玖玖在线资源| 精品国产一区二区三区麻豆小说 | 99久久婷婷国产精品综合| 三级视频网站在线观看| 91在线观看污| 自拍偷拍视频亚洲| 国产精品久久久久久久浪潮网站 | 五月天精品一区二区三区| 久久久精品福利| 欧美色涩在线第一页| www.蜜臀av| 日韩精品免费一线在线观看| 啊v视频在线| 裸体女人亚洲精品一区| 国产va在线视频| 国产精品吊钟奶在线| 亚洲免费一区三区| 久久精品国产理论片免费| 欧美日韩第一| 免费看日b视频| 国产欧美一级| 污污的网站免费| 懂色一区二区三区免费观看| 精品少妇人妻一区二区黑料社区 | 日本少妇做爰全过程毛片| 欧美性感美女h网站在线观看免费| 国模私拍一区二区| 日韩欧美国产一二三区| 视频二区在线| 亚洲性视频网站| dy888亚洲精品一区二区三区| 91精品国产电影| 欧美一区二区三区婷婷| 国产精品免费一区二区三区在线观看| 视频精品在线观看| 黄色录像特级片| 日本中文字幕一区| 成人做爰69片免费| 中文字幕国产一区| 四虎成人精品永久免费av| 欧美性淫爽ww久久久久无| 丰满熟女一区二区三区| 夜夜嗨av一区二区三区免费区| √天堂8在线网| 日韩美女视频免费看| 超碰在线亚洲| 一本一本久久a久久精品综合妖精| 一本久久知道综合久久| 中文字幕一区二区在线观看视频| 91视频.com| 久久久久久久国产精品毛片| 欧美日韩亚洲综合在线| 婷婷久久久久久| 久久99国产综合精品女同| 欧美激情啪啪| 日韩国产欧美精品| 亚洲欧美成人| 亚洲色偷偷色噜噜狠狠99网| 亚洲欧美日本在线| 中文字幕 日韩有码| 精品亚洲aⅴ在线观看| 免费在线观看的电影网站| 国产美女扒开尿口久久久| 蜜乳av综合| 精品久久久久久久久久中文字幕| 国产电影一区在线| 精品无码久久久久成人漫画| 色美美综合视频| 色在线免费视频| 久久久伊人欧美| 涩爱av色老久久精品偷偷鲁 | 国产精品免费在线| 欧美日本三区| 好吊操视频这里只有精品| 亚洲精品日产精品乱码不卡| 亚洲一二区视频| 中文字幕久久久av一区| 日韩大片欧美大片| 欧美一区二视频在线免费观看| 夜夜嗨一区二区三区| 中文字幕乱码一区| 婷婷久久综合九色综合绿巨人| 韩国av在线免费观看| 久久久久久久久91| 国产精品视屏| 欧美视频免费看欧美视频| 99精品国产热久久91蜜凸| 日韩久久久久久久久| 日韩av网站在线| 欧美gay囗交囗交| 色综合电影网| 久久精品国产亚洲高清剧情介绍| 国产精品18在线| 3atv一区二区三区| 怡红院在线播放| 国产精品久久波多野结衣| 亚洲每日更新| 亚洲第一香蕉网| 欧美三级蜜桃2在线观看| 免费黄网站在线| 51蜜桃传媒精品一区二区| 欧美极品一区二区三区| 在线播放av网址| 色综合久久久久综合体| 9色在线观看| 亚洲字幕一区二区| 亚洲精品四区| 欧美18—19性高清hd4k| 欧美日韩一级黄| 日本大片在线播放| 欧美二区在线看| 免费成人美女在线观看.| 欧美三根一起进三p| 亚洲国产精品专区久久| 天天综合网天天| 99精品视频网站| av成人老司机| 中文字幕无码乱码人妻日韩精品| 久久精品国产一区二区电影| 91精品久久久久久综合五月天| 黄色www网站| 中文av字幕一区| 色一情一乱一区二区三区| 日韩av手机在线| 国产精品v日韩精品v欧美精品网站| 先锋资源av在线| 欧美老女人第四色| 国模精品视频| 黄色www在线观看| 99在线精品观看| 96日本xxxxxⅹxxx17| 97成人在线视频| 欧美成人milf| 超碰97人人干| 欧美一级黄色片| 日韩不卡在线| 日韩中字在线观看| 综合在线观看色|