最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java并發(fā)框架中的AQS詳細(xì)解析

 更新時(shí)間:2024年01月29日 11:04:36   作者:Smallc0de  
這篇文章主要介紹了Java并發(fā)框架中的AQS詳細(xì)解析,之前說鎖的升級的時(shí)候,說到了自旋鎖會空轉(zhuǎn)幾次嘗試等待獲取資源,其實(shí)這一系列的動作是有一個(gè)規(guī)范的這個(gè)規(guī)范叫做同步發(fā)生器AbstractQueuedSynchronizer ,簡稱AQS,需要的朋友可以參考下

前言

之前說鎖的升級的時(shí)候,說到了自旋鎖會空轉(zhuǎn)幾次嘗試等待獲取資源,其實(shí)這一系列的動作是有一個(gè)規(guī)范的這個(gè)規(guī)范叫做同步發(fā)生器AbstractQueuedSynchronizer ,簡稱AQS。

同步發(fā)生器是用來構(gòu)建鎖用的,可以說是Java中同步組件的基礎(chǔ),在Java JDK的JUC包中:java.util.concurrent。

我們常用的ReentrantLock,Semaphore等等用的都是這樣一個(gè)架構(gòu),可以說是這些Lock工具的一個(gè)基礎(chǔ)。

AQS基本思想

AQS是一個(gè)隊(duì)列管理器,通過內(nèi)置的FIFO同步隊(duì)列去管理線程爭奪資源的。

FIFO就是先進(jìn)先出(Frist In First Out)的縮寫。

其實(shí)現(xiàn)核心是用到了CHL同步隊(duì)列,其核心思想就是:把每一個(gè)線程看作一個(gè)節(jié)點(diǎn)Node,然后給這些節(jié)點(diǎn)上加上前驅(qū)指針和后繼指針,這樣一來就可以用指針把這些線程節(jié)點(diǎn)(Thread Node)連接起來形成一個(gè)雙向鏈表,或者說雙向隊(duì)列。

除此之外,還有一個(gè)同步器節(jié)點(diǎn)(Synchronized Node),用來管理這些線程節(jié)點(diǎn)。

同步器節(jié)點(diǎn)也有兩個(gè)指針,第一個(gè)指針指向隊(duì)列首節(jié)點(diǎn),第二個(gè)指針指向隊(duì)列尾節(jié)點(diǎn)。

因此同步器節(jié)點(diǎn)是事實(shí)上的頭節(jié)點(diǎn)Head,下圖就是一個(gè)完整的CHL同步隊(duì)列示意圖。

在這里插入圖片描述

注:CHL是人名簡稱沒啥具體意義。

AQS操作同步隊(duì)列

同步隊(duì)列有了,誰能拿到資源則是由一個(gè)狀態(tài)變量(state)來確定的。

當(dāng)state==0時(shí),表示當(dāng)前資源沒有線程占用;當(dāng)state>=1時(shí),表示當(dāng)前資源已經(jīng)被占用了,其他線程必須等待資源釋放。

假設(shè)有一個(gè)線程要使用資源,首先先會去檢查state變量獲取結(jié)果,如果state==0說明該線程可以使用請求資源,不需要排隊(duì),直接取出線程節(jié)點(diǎn)去執(zhí)行;如果state>=1,說明該資源已經(jīng)被前面的線程拿走了,就必須要排隊(duì)。

在這里插入圖片描述

既然有個(gè)這個(gè)概念,所以說每一個(gè)線程節(jié)點(diǎn)都會做這樣的事情:獲取鎖和釋放鎖。

每個(gè)在隊(duì)列里面的縣城節(jié)點(diǎn),都會不斷地自旋,每次自旋結(jié)束都會嘗試獲取鎖,如果獲取不到那么繼續(xù)自旋。

由于是FIFO先進(jìn)先出這種公平模式,因此線程頭節(jié)點(diǎn)總會第一個(gè)獲取到鎖,以此類推。

這里所有的節(jié)點(diǎn)都在[ 嘗試獲取鎖 – 自旋 ] 這種狀態(tài)不斷地重復(fù)。但是由于使用FIFO模式,只有頭節(jié)點(diǎn)的自旋是有意義的,其他的就是在空轉(zhuǎn)。

在這里插入圖片描述

這樣做有什么好處呢?假設(shè)我們把所有沒有獲取到資源的線程都掛起,這就必然要經(jīng)過用戶線程和核心(系統(tǒng))線程之間的切換,這種切換是非常耗時(shí)的。

由于CPU執(zhí)行的會很快,所以預(yù)期就是自旋幾次以后,就可以拿到想要的鎖,以規(guī)避線程之間的切換。

AQS 的用法

上面說過AbstractQueuedSynchronizer是一個(gè)框架,它能干什么用還得祭出官方文檔一探究竟,官方文檔很長,我們截取兩句最重要的:

Provides a framework for implementing blocking locks and related synchronizers (semaphores, events, etc) that rely on first-in-first-out (FIFO) wait queues. 這句話是對AbstractQueuedSynchronizer定義,翻譯過來就是說:為那些想要依賴于FIFO等待隊(duì)列的阻塞鎖和相關(guān)的同步器(semaphores, events, 等等) 提供一個(gè)實(shí)施框架。 Subclasses should be defined as non-public internal helper classes that are used to implement the synchronization properties of their enclosing class. 這句話是用法的概括:其子類應(yīng)該被定義為非共有的內(nèi)部幫助器(助手類),用于實(shí)現(xiàn)外部類的屬性同步。

說白了AbstractQueuedSynchronizer就是Java給開發(fā)人員提供一個(gè)獲取鎖和釋放鎖的模板,用來處理synchronized封鎖粒度過大的問題。

它的主要功能方法如下:

Modifier and TypeMethodDescription
voidacquire(int arg)Acquires in exclusive mode, ignoring interrupts. 獨(dú)占模式獲取對象,忽略中斷。
voidacquireShared(int arg)Acquires in shared mode, ignoring interrupts. 共享模式獲取對象,忽略中斷。
booleanrelease(int arg)Releases in exclusive mode.以獨(dú)占模式釋放對象。
booleanreleaseShared(int arg)Releases in shared mode.以共享模式釋放對象。
protected booleantryAcquire(int arg)Attempts to acquire in exclusive mode.試圖以獨(dú)占模式獲取鎖,這個(gè)就是自旋的方法,一直試探
protected inttryAcquireShared(int arg)Attempts to acquire in shared mode. 試圖以共享模式獲取鎖。

說明:共享模式下,當(dāng)一個(gè)線程獲取了鎖,其他線程依然可讀取信息。獨(dú)占模式下,線程獨(dú)占了鎖,不許其他線程使用。

源碼解析AQS

上面已經(jīng)AQS原理和常用方法說完了,總要有一個(gè)地方體現(xiàn)吧。我們可以打開一個(gè)方法看看Java中AQS獲取鎖是不是和我們說的原理一致。首先進(jìn)入acquire()方法。

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

進(jìn)入以后先判斷tryAcquire(arg)這里面只有一個(gè)拋異常不多說了。然后調(diào)用acquireQueued()方法,進(jìn)入。

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) { //空轉(zhuǎn),自旋
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) { //如果當(dāng)前是頭節(jié)點(diǎn),繼續(xù)嘗試獲取鎖,這也就是為啥只有頭節(jié)點(diǎn)才有意義
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
		//如果不是繼續(xù)等待
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

發(fā)現(xiàn)這個(gè)方法已經(jīng)要求傳入的參數(shù)就是Node,其實(shí)就是把要等待的Node繼續(xù)等待,那么返回上去,看看addWaiter()又寫了啥。

private Node addWaiter(Node mode) {
	//把當(dāng)前線程改造稱為Node,
    Node node = new Node(Thread.currentThread(), mode);
    // Try the fast path of enq; backup to full enq on failure
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    enq(node);
    return node;
}

進(jìn)入就發(fā)現(xiàn)用構(gòu)建Node用的就是當(dāng)前線程Thread.currentThread(),這就是線程節(jié)點(diǎn)的由來。

實(shí)現(xiàn)一個(gè)鎖類

在Java的JUC包中同樣為了這樣一個(gè)模板提供了一個(gè)實(shí)現(xiàn)接口就是Lock接口,我們常用的ReentrantLock就是實(shí)現(xiàn)了這個(gè)接口構(gòu)建出來的,不妨按照這樣一個(gè)模板自己構(gòu)建一個(gè)加深理解。

首先看下Lock接口里面都有什么方法:

Modifier and TypeMethodDescription
voidlock()Acquires the lock. 獲取鎖。
voidlockInterruptibly()Acquires the lock unless the current thread is interrupted. 獲取鎖,除非當(dāng)前線程被中斷。
ConditionnewCondition()Returns a new Condition instance that is bound to this Lock instance. 返回一個(gè)綁定Condition實(shí)例的鎖。
booleantryLock()Acquires the lock only if it is free at the time of invocation. 當(dāng)調(diào)用時(shí)鎖為空閑,才能獲取鎖。嘗試獲取鎖。
booleantryLock(long time, TimeUnit unit)Acquires the lock if it is free within the given waiting time and the current thread has not been interrupted.在給定時(shí)間內(nèi)處于空閑狀態(tài)且當(dāng)前線程沒有被中斷時(shí),才能獲取鎖。嘗試獲取鎖+超時(shí)時(shí)間。
voidunlock()Releases the lock. 釋放鎖。

按照上面的模板,我們可以自己構(gòu)建一個(gè)鎖工具:

public class MyLock implements Lock {
    private Helper helper = new Helper();
    //按照官方文檔所說,構(gòu)建內(nèi)部幫助類
    private class Helper extends AbstractQueuedSynchronizer {
        //構(gòu)建嘗試獲取鎖的方法
        @Override
        protected boolean tryAcquire(int arg) {
            int state = getState();
            if (0 == state) {
                //利用cas的原理修改state
                if (compareAndSetState(0, arg)) {
                    //設(shè)置當(dāng)前線程擁有資源
                    setExclusiveOwnerThread(Thread.currentThread());
                    return true;
                }
            }//同一個(gè)線程獲取被鎖住的資源時(shí),直接分配給這個(gè)線程,實(shí)現(xiàn)可重入性
            else if(getExclusiveOwnerThread()==Thread.currentThread()){ //刪除這個(gè)else if條件,就會變?yōu)橐粋€(gè)不可重入鎖
                setState(getState()+arg);
                return true;
            }
            return false;
        }
        //構(gòu)建嘗試釋放鎖的方法
        @Override
        protected boolean tryRelease(int arg) {
            //arg是傳遞進(jìn)來的state的期望值
            int state = getState() - arg;
            //判斷釋放后狀態(tài)是否為0
            if (0 == state) {
                setExclusiveOwnerThread(null);
                setState(0);
                return true;
            }
            //因?yàn)樾枰薷牡木€程就是當(dāng)前占有鎖的線程,所以此時(shí)直接重置是沒有線程安全問題的,也就是當(dāng)前線程獨(dú)占了資源state
            setState(state);
            return false;
        }
        public Condition newConditionObject(){
            return new ConditionObject();
        }
    }
    //加鎖方法
    @Override
    public void lock() {
        helper.acquire(1); //AbstractQueuedSynchronizer原生方法
    }
    //鎖中斷
    @Override
    public void lockInterruptibly() throws InterruptedException {
        helper.acquireInterruptibly(1); //AbstractQueuedSynchronizer原生方法
    }
	//嘗試獲取鎖
    @Override
    public boolean tryLock() {
        return helper.tryAcquire(1); //使用自己實(shí)現(xiàn)的tryAcquire()方法
    }
    //嘗試加鎖
    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return helper.tryAcquireNanos(1,unit.toNanos(time)); //AbstractQueuedSynchronizer原生方法
    }
	//釋放鎖
    @Override
    public void unlock() {
        helper.tryRelease(1);  //使用自己實(shí)現(xiàn)的tryRelease()方法
    }
	//條件
    @Override
    public Condition newCondition() {
        return helper.newConditionObject(); //AbstractQueuedSynchronizer原生方法
    }
}

總結(jié)

到此AQS的內(nèi)容告一段落。這篇博客主要講了AQS的設(shè)計(jì)思想,以及操作同步隊(duì)列的方式,同時(shí)完成了一個(gè)簡單的鎖幫助類,希望能夠幫助大家更好的理解鎖這一個(gè)同步機(jī)制,以及由AQS架構(gòu)為基礎(chǔ)的各種鎖工具的內(nèi)部原理。

到此這篇關(guān)于Java并發(fā)框架中的AQS詳細(xì)解析的文章就介紹到這了,更多相關(guān)Java的AQS內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java 獲取指定日期的實(shí)現(xiàn)方法總結(jié)

    Java 獲取指定日期的實(shí)現(xiàn)方法總結(jié)

    以下是對Java中獲取指定日期的實(shí)現(xiàn)方法進(jìn)行了歸納總結(jié),需要的朋友可以參考下
    2013-07-07
  • Java面向?qū)ο蟪绦蛟O(shè)計(jì):類的定義,靜態(tài)變量,成員變量,構(gòu)造函數(shù),封裝與私有,this概念與用法詳解

    Java面向?qū)ο蟪绦蛟O(shè)計(jì):類的定義,靜態(tài)變量,成員變量,構(gòu)造函數(shù),封裝與私有,this概念與用法詳解

    這篇文章主要介紹了Java面向?qū)ο箢惖亩x,靜態(tài)變量,成員變量,構(gòu)造函數(shù),封裝與私有,this概念與用法,較為詳細(xì)的分析了Java類的定義,靜態(tài)變量,成員變量,構(gòu)造函數(shù),封裝,私有等相關(guān)原理、用法及操作注意事項(xiàng),需要的朋友可以參考下
    2020-04-04
  • Java泛型使用一些常見報(bào)錯(cuò)總結(jié)

    Java泛型使用一些常見報(bào)錯(cuò)總結(jié)

    泛型是Java中一種強(qiáng)大的特性,它允許我們編寫類型安全且可重用的代碼,這篇文章主要介紹了Java泛型使用一些常見報(bào)錯(cuò)總結(jié)的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2026-01-01
  • java字節(jié)碼框架ASM的深入學(xué)習(xí)

    java字節(jié)碼框架ASM的深入學(xué)習(xí)

    這篇文章主要給大家介紹了java中字節(jié)碼框架ASM的相關(guān)資料,文中介紹的非常詳細(xì),相信對大家的理解和學(xué)習(xí)具有一定的參考借鑒價(jià)值,有需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-01-01
  • java實(shí)現(xiàn)即時(shí)通信的完整步驟分享

    java實(shí)現(xiàn)即時(shí)通信的完整步驟分享

    這篇文章主要給大家介紹了關(guān)于java實(shí)現(xiàn)即時(shí)通信的完整步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • SSH原理及兩種登錄方法圖文詳解

    SSH原理及兩種登錄方法圖文詳解

    SSH(Secure Shell)是一套協(xié)議標(biāo)準(zhǔn),可以用來實(shí)現(xiàn)兩臺機(jī)器之間的安全登錄以及安全的數(shù)據(jù)傳送,其保證數(shù)據(jù)安全的原理是非對稱加密。本文通過圖文并茂的形式給大家介紹了SSH原理及兩種登錄方法,一起看看吧
    2018-08-08
  • java如何通過IP解析地理位置

    java如何通過IP解析地理位置

    這篇文章主要介紹了java如何通過IP解析地理位置的實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • Java使用hutool工具實(shí)現(xiàn)驗(yàn)證碼登錄

    Java使用hutool工具實(shí)現(xiàn)驗(yàn)證碼登錄

    這篇文章主要為大家詳細(xì)介紹了Java如何使用hutool工具實(shí)現(xiàn)驗(yàn)證碼登錄功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-12-12
  • Java中一些常見的并發(fā)集合類的使用

    Java中一些常見的并發(fā)集合類的使用

    并發(fā)集合是一種特殊的數(shù)據(jù)結(jié)構(gòu),它允許多個(gè)線程安全地訪問和修改,本文主要介紹了Java中一些常見的并發(fā)集合類的使用,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-06-06
  • Java編程之如何通過JSP實(shí)現(xiàn)頭像自定義上傳

    Java編程之如何通過JSP實(shí)現(xiàn)頭像自定義上傳

    之前做這個(gè)頭像上傳功能還是花了好多時(shí)間的,今天我將我的代碼分享給大家,下面這篇文章主要給大家介紹了關(guān)于Java編程之如何通過JSP實(shí)現(xiàn)頭像自定義上傳的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-12-12

最新評論

青冈县| 定日县| 田阳县| 柳江县| 沁源县| 蚌埠市| 阳朔县| 岫岩| 金塔县| 孙吴县| 彰武县| 彩票| 阳高县| 六盘水市| 石渠县| 含山县| 漯河市| 梁河县| 普兰店市| 惠来县| 宜兰市| 公主岭市| 密山市| 甘南县| 衡阳县| 同仁县| 平定县| 彩票| 桦川县| 邻水| 大足县| 大姚县| 南投市| 广德县| 开化县| 汶上县| 葵青区| 鹤峰县| 五台县| 文山县| 阳高县|