欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

前言

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

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

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

AQS基本思想

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

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

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

除此之外,還有一個(gè)同步器節(jié)點(diǎn)(Synchronized Node),用來(lái)管理這些線程節(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是人名簡(jiǎn)稱沒(méi)啥具體意義。

AQS操作同步隊(duì)列

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

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

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

在這里插入圖片描述

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

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

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

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

在這里插入圖片描述

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

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

AQS 的用法

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

Provides a framework for implementing blocking locks and related synchronizers (semaphores, events, etc) that rely on first-in-first-out (FIFO) wait queues. 這句話是對(duì)AbstractQueuedSynchronizer定義,翻譯過(guò)來(lái)就是說(shuō):為那些想要依賴于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)外部類的屬性同步。

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

它的主要功能方法如下:

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

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

源碼解析AQS

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

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

進(jìn)入以后先判斷tryAcquire(arg)這里面只有一個(gè)拋異常不多說(shuō)了。然后調(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)的由來(lái)。

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

在Java的JUC包中同樣為了這樣一個(gè)模板提供了一個(gè)實(shí)現(xiàn)接口就是Lock接口,我們常用的ReentrantLock就是實(shí)現(xiàn)了這個(gè)接口構(gòu)建出來(lái)的,不妨按照這樣一個(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)前線程沒(méi)有被中斷時(shí),才能獲取鎖。嘗試獲取鎖+超時(shí)時(shí)間。
voidunlock()Releases the lock. 釋放鎖。

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

public class MyLock implements Lock {
    private Helper helper = new Helper();
    //按照官方文檔所說(shuō),構(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條件,就會(huì)變?yōu)橐粋€(gè)不可重入鎖
                setState(getState()+arg);
                return true;
            }
            return false;
        }
        //構(gòu)建嘗試釋放鎖的方法
        @Override
        protected boolean tryRelease(int arg) {
            //arg是傳遞進(jìn)來(lái)的state的期望值
            int state = getState() - arg;
            //判斷釋放后狀態(tài)是否為0
            if (0 == state) {
                setExclusiveOwnerThread(null);
                setState(0);
                return true;
            }
            //因?yàn)樾枰薷牡木€程就是當(dāng)前占有鎖的線程,所以此時(shí)直接重置是沒(méi)有線程安全問(wèn)題的,也就是當(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è)簡(jiǎn)單的鎖幫助類,希望能夠幫助大家更好的理解鎖這一個(gè)同步機(jī)制,以及由AQS架構(gòu)為基礎(chǔ)的各種鎖工具的內(nèi)部原理。

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

相關(guān)文章

  • Java Comparator.comparing比較導(dǎo)致空指針異常的解決

    Java Comparator.comparing比較導(dǎo)致空指針異常的解決

    這篇文章主要介紹了Java Comparator.comparing比較導(dǎo)致空指針異常的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • springboot整合JavaCV實(shí)現(xiàn)視頻截取第N幀并保存圖片

    springboot整合JavaCV實(shí)現(xiàn)視頻截取第N幀并保存圖片

    這篇文章主要為大家詳細(xì)介紹了springboot如何整合JavaCV實(shí)現(xiàn)視頻截取第N幀并保存為圖片,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下
    2023-08-08
  • 關(guān)于java String中intern的深入講解

    關(guān)于java String中intern的深入講解

    這篇文章主要給大家介紹了關(guān)于java String中intern的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • 淺談Java 繼承接口同名函數(shù)問(wèn)題

    淺談Java 繼承接口同名函數(shù)問(wèn)題

    這篇文章主要介紹了淺談Java 繼承接口同名函數(shù)問(wèn)題。具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-09-09
  • SpringBoot自定義FailureAnalyzer過(guò)程解析

    SpringBoot自定義FailureAnalyzer過(guò)程解析

    這篇文章主要介紹了SpringBoot自定義FailureAnalyzer,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • SpringBoot執(zhí)行有返回值的異步任務(wù)問(wèn)題

    SpringBoot執(zhí)行有返回值的異步任務(wù)問(wèn)題

    這篇文章主要介紹了SpringBoot執(zhí)行有返回值的異步任務(wù)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • 關(guān)于如何搭建CAS服務(wù)并將CAS項(xiàng)目導(dǎo)入IDEA

    關(guān)于如何搭建CAS服務(wù)并將CAS項(xiàng)目導(dǎo)入IDEA

    這篇文章主要介紹了關(guān)于如何搭建CAS服務(wù)并將CAS項(xiàng)目導(dǎo)入IDEA的問(wèn)題,文中提供了詳細(xì)的圖文講解,需要的朋友可以參考下,如果有錯(cuò)誤的地方還請(qǐng)指正
    2023-03-03
  • Java序列化原理詳解

    Java序列化原理詳解

    這篇文章主要介紹了Java序列化原理詳解,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,感興趣的小伙伴可以參考一下
    2022-06-06
  • java生成指定范圍的隨機(jī)日期

    java生成指定范圍的隨機(jī)日期

    這篇文章主要為大家詳細(xì)介紹了java生成指定范圍的隨機(jī)日期,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-12-12
  • 詳解Java?@Documented注解的作用

    詳解Java?@Documented注解的作用

    @Documented和@Deprecated注解長(zhǎng)得有點(diǎn)像,@Deprecated是用來(lái)標(biāo)注某個(gè)類或者方法不建議再繼續(xù)使用,@Documented只能用在注解上,本文將通過(guò)示例詳細(xì)說(shuō)說(shuō)@Documented注解的作用,需要的可以參考一下
    2022-09-09

最新評(píng)論