www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當(dāng)前位置:首頁 > 公眾號精選 > 架構(gòu)師社區(qū)
[導(dǎo)讀]AQS簡介 AQS(AbstractQueuedSynchronizer)為「抽象隊列同步器」,簡單的說「AQS就是一個抽象類」,抽象類就是AbstractQueuedSynchronizer,沒有實現(xiàn)任何的接口,「僅僅定義了同步狀態(tài)(state)的獲取和釋放的方法」。 它提供了一個「FIFO隊列」,多線程競爭


AQS簡介

AQS(AbstractQueuedSynchronizer)為「抽象隊列同步器」,簡單的說「AQS就是一個抽象類」,抽象類就是AbstractQueuedSynchronizer,沒有實現(xiàn)任何的接口,「僅僅定義了同步狀態(tài)(state)的獲取和釋放的方法」。

它提供了一個「FIFO隊列」,多線程競爭資源的時候,沒有競爭到的線程就會進(jìn)入隊列中進(jìn)行等待,并且「定義了一套多線程訪問共享資源的同步框架」。

在AQS中的鎖類型有兩種:分別是「Exclusive(獨占鎖)「和」Share(共享鎖)」。

「獨占鎖」就是「每次都只有一個線程運行」,如ReentrantLock。

「共享鎖」就是「同時可以多個線程運行」,如Semaphore、CountDownLatch、ReentrantReadWriteLock。

AQS源碼分析

在AQS的源碼可以看到對于「state共享變量」,使用「volatile關(guān)鍵字」進(jìn)行修飾,從而「保證了可見性」。從上面的源碼中可以看出,對于state的修改操作提供了setState和compareAndSetState,「那么為什么要提供這兩個對state的修改呢?」

因為compareAndSetState方法「通常使用在獲取到鎖之前」,當(dāng)前線程不是鎖持有者,對于state的修改可能存在線程安全問題,所以「需要保證對state修改的原子性操作」。

setState方法通常用于當(dāng)前正持有鎖的線程對「state共享變量」進(jìn)行修改,因為不存在競爭,是線程安全的,所以沒必要使用CAS操作。

分析了AQS的源碼的實現(xiàn),接下來我們看看AQS的實現(xiàn)的原理。這里AQS的實現(xiàn)源碼和理論都會比較簡單,因為還沒有涉及到具體的實現(xiàn)類。

AQS實現(xiàn)原理

上面說到AQS中維護(hù)了一個「FIFO隊列」,并且「該隊列是一個雙向鏈表」,鏈表中的每一個節(jié)點為「Node節(jié)點」,「Node類是AbstractQueuedSynchronizer中的一個內(nèi)部類」。

讓我們來看看AQS中Node內(nèi)部類的源碼,這樣有助于我們能夠?qū)QS的內(nèi)部的實現(xiàn)更加的清晰:

static final class Node {

        static final Node SHARED = new Node();
        static final Node EXCLUSIVE = null;
        static final int CANCELLED =  1;
        static final int SIGNAL    = -1;
        static final int CONDITION = -2;
        static final int PROPAGATE = -3;
        volatile int waitStatus;
        volatile Node prev;
        volatile Node next;
        volatile Thread thread;
        Node nextWaiter;

        final boolean isShared() {
            return nextWaiter == SHARED;
        }

        final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }

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

        Node(Thread thread, Node mode) {     // Used by addWaiter
            this.nextWaiter = mode;
            this.thread = thread;
        }

        Node(Thread thread, int waitStatus) { // Used by Condition
            this.waitStatus = waitStatus;
            this.thread = thread;
        }
    }

可以看到上面的Node類比較簡單,只是對于每個Node節(jié)點擁有的屬性進(jìn)行維護(hù),在Node內(nèi)部類中最重要的基本構(gòu)成就是這幾個:

volatile Node prev;
    volatile Node next;
    volatile Thread thread;

「根據(jù)源碼的分析和線程的競爭共享資源的原理」,關(guān)于AQS的實現(xiàn)原理,我這里畫了一張圖:在FIFO隊列中,「頭節(jié)點占有鎖」,也就是頭節(jié)點才是鎖的持有者,尾指針指向隊列的最后一個等待線程節(jié)點,除了頭節(jié)點和尾節(jié)點,節(jié)點之間都有「前驅(qū)指針」「后繼指針」

在AQS中維護(hù)了一個「共享變量state」,標(biāo)識當(dāng)前的資源是否被線程持有,多線程競爭的時候,會去判斷state是否為0,嘗試的去把state修改為1

分析了AQS的源碼的實現(xiàn)和原理實現(xiàn),但是AQS里面具體是沒有做同步的具體實現(xiàn),如果要什么了解AQS的具體的實現(xiàn)原理,要需要看AQS的具體實現(xiàn)類,這邊就以ReentrantLock為例。

ReentrantLock實現(xiàn)原理

如果多線程在競爭共享資源時,「競爭失敗的線程就會添加入FIFO隊列的尾部」

ReentrantLock的的具體實現(xiàn)中,這邊以在ReentrantLock的非公平鎖的實現(xiàn)為例,因為公平鎖的實現(xiàn),之前已經(jīng)寫過一篇文章分析過了。

我們來看看新添加節(jié)點的源碼寫的實現(xiàn)邏輯:當(dāng)競爭鎖資源的線程失敗后直接進(jìn)入acquire(1)方法,從源碼中可以看出,acquire(1)的實現(xiàn)主要有這三步的邏輯:

  1. tryAcquire(arg):嘗試再次獲取鎖。
  2. addWaiter(Node.EXCLUSIVE):若是獲取鎖失敗,就會將當(dāng)前線程組裝成一個Node節(jié)點,進(jìn)行入隊操作。
  3. acquireQueued(addWaiter(Node.EXCLUSIVE), arg)):acquireQueued方法以addWaiter返回的頭節(jié)點作為參數(shù),內(nèi)部實現(xiàn)進(jìn)行鎖自旋,以及判斷是否應(yīng)該執(zhí)行線程掛起。

下面我們再來看看tryAcquire(arg)的源碼,從上面的看一看出arg的值為1,具體的實現(xiàn)源碼如下:從源碼的分析中可以看出,tryAcquire(arg)的實現(xiàn)也就是判斷state的值是否已經(jīng)被釋放,「若釋放則當(dāng)前線程就會CAS操作將state設(shè)置為1,若是沒有釋放,就會判斷是否可以進(jìn)行鎖的重入」。

分析完tryAcquire(arg)的實現(xiàn),來看看addWaiter:對于新加入的線程添加到雙向鏈表中使用尾插法。當(dāng)線程加入隊列中,主要進(jìn)行這幾步操作「新加入的節(jié)點prev指針指向原來的tail節(jié)點,原來的tail節(jié)點的next指針指向新加入的節(jié)點」,這個也就是常見的「雙向鏈表尾插法」的操作。

「最后把tail指向新加入的節(jié)點」,如此一來就完成了新加入節(jié)點的入隊操作,接下來我們接著分析源碼。

當(dāng)然這里的前提是「隊列中不為空」,若是為空的話,不會走上面的邏輯,而是走enq(node),進(jìn)行初始化節(jié)點,我們來看看enq(node)操作:執(zhí)行完上面的入對操作后,接著執(zhí)行acquireQueued方法,來看看它的具體實現(xiàn)源碼:從上上面的源碼中可以看出,涉及到「頭節(jié)點head的出隊」操作,并且將「當(dāng)前線程的node節(jié)點晉升為head節(jié)點」。

因為只有「頭節(jié)點才是鎖的持有者」,所以對于head節(jié)點的出隊操作,head的指向會隨時改變:會把「原來的頭節(jié)點進(jìn)行出隊操作,也就是把原來的頭節(jié)點next指針指向null,原來第二節(jié)點的prev指針指向null」

最后把head指針指向第二節(jié)點,當(dāng)然thread2同時還會修改共享狀態(tài)變量state的值,如此一來就完成了鎖的釋放。

當(dāng)釋放完鎖之后,就會執(zhí)行shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt()判斷當(dāng)前的線程是否應(yīng)該被掛起,我們來看看它的源碼實現(xiàn):在shouldParkAfterFailedAcquire中的實現(xiàn),當(dāng)前驅(qū)節(jié)點的狀態(tài)量waitStatus為SIGNAL的時候,就會掛起。通過上面的分析對于AQS的實現(xiàn)基本有比較清晰的認(rèn)識,主要是對實現(xiàn)類ReentrantLock的實現(xiàn)原理進(jìn)行深入的分析,并且是基于「非公平鎖」「獨占鎖」的實現(xiàn)。

在AQS的底層維護(hù)了一個「FIFO隊列」,多線程競爭共享資源的時候,「失敗的線程會被添加入隊列中」「非公平鎖」實現(xiàn)中,新加入的線程節(jié)點會「自旋」嘗試的獲取鎖。

分析完AQS我們來分析CAS,「那么什么是CAS呢?」

CAS簡介

在分析ReentrantLock的具體實現(xiàn)的源碼中,可以看出所有涉及設(shè)置共享變量的操作,都會指向CAS操作,保證原子性操作。

CAS(compare and swap)原語理解就是比較并交換的意思,「CAS是一種樂觀鎖的實現(xiàn)」。

在CAS的算法實現(xiàn)中有三個值:「更新的變量」「舊的值」、「新值」。在修改共享資源時候,會與原值進(jìn)行比較,若是等于原值,就修改為新值。

于是在這里的算法實現(xiàn)下,即使不加鎖,也能保證數(shù)據(jù)的可見性,即使的發(fā)現(xiàn)數(shù)據(jù)是否被更改,若是數(shù)據(jù)已經(jīng)被更新則寫操作失敗。

但是CAS也會引發(fā)ABA的問題,「什么是ABA問題呢?」 不慌請聽我詳細(xì)道來

ABA問題

ABA問題就是假如有兩個線程,同一時間讀取一個共享變量state=1,此時兩個線程都已經(jīng)將state的副本賦值到自己的工作內(nèi)存中。

當(dāng)線程一對state修改state=state+1,并且寫入到主存中,然后線程一又對state=state-1寫入到主存,此時主存的state是變化了兩次,只不過又變回了原來的值。

那么此時線程二修改state的時候就會修改成功,這就是ABA問題。對于「ABA問題的解決方案就是加版本號(version)」,每次進(jìn)行比較的時候,也會比較版本號。

因為版本版是只增不減,比如以時間作為版本號,每一時刻的時間都不一樣,這樣就能避免ABA的問題。

CAS性能分析

相對于「synchronized的阻塞算法」的實現(xiàn),「CAS采用的是樂觀鎖的非阻塞算法」的實現(xiàn),一般CPU在進(jìn)行線程的上下文切換的時間比執(zhí)行CPU的指令集的時間長,所以CAS操作在性能上也有了很大的提升。

但是所有的算法都是沒有最完美的,在執(zhí)行CAS的操作中,沒有更新成功的就會自旋,這樣也會消耗CPU的資源,對于CPU來說是不友好的。

特別推薦一個分享架構(gòu)+算法的優(yōu)質(zhì)內(nèi)容,還沒關(guān)注的小伙伴,可以長按關(guān)注一下:

深入剖析AQS和CAS,看了都說好

長按訂閱更多精彩▼

深入剖析AQS和CAS,看了都說好

如有收獲,點個在看,誠摯感謝

免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
關(guān)閉
關(guān)閉