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

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


AQS簡(jiǎn)介

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

它提供了一個(gè)「FIFO隊(duì)列」,多線(xiàn)程競(jìng)爭(zhēng)資源的時(shí)候,沒(méi)有競(jìng)爭(zhēng)到的線(xiàn)程就會(huì)進(jìn)入隊(duì)列中進(jìn)行等待,并且「定義了一套多線(xiàn)程訪(fǎng)問(wèn)共享資源的同步框架」。

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

「獨(dú)占鎖」就是「每次都只有一個(gè)線(xiàn)程運(yùn)行」,如ReentrantLock。

「共享鎖」就是「同時(shí)可以多個(gè)線(xiàn)程運(yùn)行」,如Semaphore、CountDownLatch、ReentrantReadWriteLock

AQS源碼分析

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

因?yàn)?code style="font-size: 14px;word-wrap: break-word;margin-right: 2px;margin-left: 2px;font-family: " operator="" mono",="" consolas,="" monaco,="" menlo,="" monospace;word-break:="" break-all;color:="" rgb(53,="" 148,="" 247);background:="" rgba(59,="" 170,="" 250,="" 0.1);display:="" inline-block;padding-right:="" 2px;padding-left:="" 2px;border-radius:="" 2px;height:="" 21px;line-height:="" 22px;"="">compareAndSetState方法「通常使用在獲取到鎖之前」,當(dāng)前線(xiàn)程不是鎖持有者,對(duì)于state的修改可能存在線(xiàn)程安全問(wèn)題,所以「需要保證對(duì)state修改的原子性操作」。

setState方法通常用于當(dāng)前正持有鎖的線(xiàn)程對(duì)「state共享變量」進(jìn)行修改,因?yàn)椴淮嬖诟?jìng)爭(zhēng),是線(xiàn)程安全的,所以沒(méi)必要使用CAS操作。

分析了AQS的源碼的實(shí)現(xiàn),接下來(lái)我們看看AQS的實(shí)現(xiàn)的原理。這里AQS的實(shí)現(xiàn)源碼和理論都會(huì)比較簡(jiǎn)單,因?yàn)檫€沒(méi)有涉及到具體的實(shí)現(xiàn)類(lèi)。

AQS實(shí)現(xiàn)原理

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

讓我們來(lái)看看AQS中Node內(nèi)部類(lèi)的源碼,這樣有助于我們能夠?qū)QS的內(nèi)部的實(shí)現(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類(lèi)比較簡(jiǎn)單,只是對(duì)于每個(gè)Node節(jié)點(diǎn)擁有的屬性進(jìn)行維護(hù),在Node內(nèi)部類(lèi)中最重要的基本構(gòu)成就是這幾個(gè):

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

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

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

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

ReentrantLock實(shí)現(xiàn)原理

如果多線(xiàn)程在競(jìng)爭(zhēng)共享資源時(shí),「競(jìng)爭(zhēng)失敗的線(xiàn)程就會(huì)添加入FIFO隊(duì)列的尾部」。

ReentrantLock的的具體實(shí)現(xiàn)中,這邊以在ReentrantLock的非公平鎖的實(shí)現(xiàn)為例,因?yàn)楣芥i的實(shí)現(xiàn),之前已經(jīng)寫(xiě)過(guò)一篇文章分析過(guò)了。

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

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

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

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

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

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

因?yàn)橹挥?strong>「頭節(jié)點(diǎn)才是鎖的持有者」,所以對(duì)于head節(jié)點(diǎn)的出隊(duì)操作,head的指向會(huì)隨時(shí)改變:會(huì)把「原來(lái)的頭節(jié)點(diǎn)進(jìn)行出隊(duì)操作,也就是把原來(lái)的頭節(jié)點(diǎn)next指針指向null,原來(lái)第二節(jié)點(diǎn)的prev指針指向null」。

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

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

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

分析完AQS我們來(lái)分析CAS,「那么什么是CAS呢?」

CAS簡(jiǎn)介

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

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

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

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

但是CAS也會(huì)引發(fā)ABA的問(wèn)題,「什么是ABA問(wèn)題呢?」 不慌請(qǐng)聽(tīng)我詳細(xì)道來(lái)

ABA問(wèn)題

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

當(dāng)線(xiàn)程一對(duì)state修改state=state+1,并且寫(xiě)入到主存中,然后線(xiàn)程一又對(duì)state=state-1寫(xiě)入到主存,此時(shí)主存的state是變化了兩次,只不過(guò)又變回了原來(lái)的值。

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

因?yàn)榘姹景媸侵辉霾粶p,比如以時(shí)間作為版本號(hào),每一時(shí)刻的時(shí)間都不一樣,這樣就能避免ABA的問(wèn)題。

CAS性能分析

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

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

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

深入剖析AQS和CAS,看了都說(shuō)好

長(zhǎng)按訂閱更多精彩▼

深入剖析AQS和CAS,看了都說(shuō)好

如有收獲,點(diǎn)個(gè)在看,誠(chéng)摯感謝

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

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