国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

java并發(fā)編程 AbstractQueuedSynchronizer(AQS)詳解一

這篇具有很好參考價值的文章主要介紹了java并發(fā)編程 AbstractQueuedSynchronizer(AQS)詳解一。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報違法"按鈕提交疑問。

1 概要

AQS在類的注釋上說的已經(jīng)很明白,提供一個框架,用于實(shí)現(xiàn)依賴先進(jìn)先出(FIFO)等待隊列的阻塞鎖和相關(guān)同步器(信號量、事件等)。此類被設(shè)計做為大多數(shù)類型的同步器的一個有用的基礎(chǔ)類,這些同步器依賴于單個原子int值(state字段)來表示狀態(tài)。
java 并發(fā)編程系列文章目錄

2 技術(shù)名詞解釋

CAS:cas是比較并交換(compare and swap)的縮寫,java對其具體實(shí)現(xiàn)是Usafe類,它有一系列的compareAndSwap方法

3 AQS核心方法原理

3.1 acquire(int arg)

從代碼邏輯上可以看到,首先嘗試獲取,如果此時返回true, 執(zhí)行結(jié)束。相當(dāng)于獲取到鎖了。tryAcquire方法是抽象方法,比如公平鎖和非公平鎖的不同實(shí)現(xiàn)邏輯
如果嘗試獲取失敗,會進(jìn)入acquireQueued(addWaiter(Node.EXCLUSIVE), arg)。

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        //沒獲取到鎖但是park住了,如果unpack且如果在等待的過程中發(fā)生中斷走到這,進(jìn)行中斷位標(biāo)記
        selfInterrupt();
}

此時具體看下acquireQueued(addWaiter(Node.EXCLUSIVE), arg)邏輯。

  1. addWaiter(Node.EXCLUSIVE)
private Node addWaiter(Node mode) {
	//創(chuà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;
        }
    }
    //走下述邏輯,上面嘗試未成功,或者此時tail == null(因?yàn)槌跏蓟臅r候都是null 在后續(xù)才會設(shè)置值)
    enq(node);
    return node;
}
private Node enq(final Node node) {
	//死循環(huán)+原子性加到列表中
    for (;;) {
        Node t = tail;
        if (t == null) { // 初始化頭結(jié)點(diǎn)和尾結(jié)點(diǎn)
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}

  1. acquireQueued(final Node node, int arg)
    因?yàn)閍ddWaiter(Node mode)最終是死循環(huán)+原子性加到列表中,是肯定成功的。也就意味著需要阻塞當(dāng)前線程了。但是有沒有挽救的余地呢?
    todo1
final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
        	//獲取當(dāng)前線程節(jié)點(diǎn)的前一個節(jié)點(diǎn),因?yàn)樵谏喜揭呀?jīng)添加到tail中了
            final Node p = node.predecessor();
            //如果此時前一個節(jié)點(diǎn)時頭節(jié)點(diǎn),因?yàn)樵诟乓幸呀?jīng)說明,先進(jìn)先出隊列,所以此時前面就一個頭
            //結(jié)點(diǎn),此時你就是阻塞隊列第一個,那沒線程阻塞啊,所以此時再嘗試下獲取鎖
            if (p == head && tryAcquire(arg)) {
            	//此時獲取到鎖,那么線程安全的情況下設(shè)置頭結(jié)點(diǎn)的node
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            //嘗試還是沒獲取到鎖,那么原子設(shè)置pred.waitStatus = -1。waitStatus 狀態(tài)下面會描述
            if (shouldParkAfterFailedAcquire(p, node) &&
            	//設(shè)置pred.waitStatus = -1成功了 會park阻塞線程,并返回該過程中是否被中斷過
                parkAndCheckInterrupt())
                //這地方是unpark 或者中斷之后的邏輯,又會回到上面去獲取鎖,如果pre不是頭節(jié)點(diǎn),又會被park住。todo1 做個標(biāo)記
                interrupted = true;
        }
    } finally {
        if (failed)//正常情況下不會到這
            cancelAcquire(node);
    }
}

總結(jié)acquire(int arg):在多線程的情況下,有一個線程tryAcquire獲取到鎖,其余的cas進(jìn)入等待隊列,當(dāng)然頭節(jié)點(diǎn)的下一個或嘗試下,沒有獲取成功的都會被park當(dāng)前線程。進(jìn)入阻塞狀態(tài)等待被喚醒

3.2 release(int arg)

先看整體邏輯,和獲取鎖是一致且相反的邏輯。先嘗試釋放鎖,釋放失敗直接false。還可以釋放失???? 如果鎖是可重入的,那么需要把state縮減成0才算釋放。
tryRelease(arg)是個接口方法,子類實(shí)現(xiàn)。

public final boolean release(int arg) {
    if (tryRelease(arg)) {
    	//如果成功了,
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}
private void unparkSuccessor(Node node) {
	//waitStatus下面描述
    int ws = node.waitStatus;
    if (ws < 0)
    	//把當(dāng)前節(jié)點(diǎn)的waitStatus修改成0,如果失敗了呢,好像也不影響,
        compareAndSetWaitStatus(node, ws, 0);

   	//s.waitStatus > 0就是線程取消狀態(tài),也即無效的節(jié)點(diǎn),再次剔除掉找到有效的節(jié)點(diǎn),從尾往頭找
    Node s = node.next;
    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;
    }
    //正常情況下從頭取等待的線程喚醒,但是如果頭節(jié)點(diǎn)的next節(jié)點(diǎn)是被取消了,那就從尾部找了,好像也不是嚴(yán)格的先進(jìn)先出啊....
    if (s != null)
        LockSupport.unpark(s.thread);
}

3.3 acquireInterruptibly(int arg)

相對于3.1的acquire(int arg),該方法會對線程獲取鎖的過程中線程發(fā)生中斷拋出異常

public final void acquireInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        //先嘗試獲取鎖,未獲取到進(jìn)度會拋異常的獲取鎖方法
        if (!tryAcquire(arg))
            doAcquireInterruptibly(arg);
}
//對比下
private void doAcquireInterruptibly(int arg)
    throws InterruptedException {
    final Node node = addWaiter(Node.EXCLUSIVE);
    boolean failed = true;
    try {
        for (;;) {
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                //區(qū)別就在這,當(dāng)parkAndCheckInterrupt()返回true時,也就是獲取鎖結(jié)果被阻塞住了,期間發(fā)生線程中斷會拋出打斷異常
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

3.3 acquireShared(int arg)

獲取共享鎖。同比上述排它鎖,相同邏輯先嘗試獲取共享鎖tryAcquireShared(arg),然后進(jìn)入doAcquireShared

public final void acquireShared(int arg) {
   if (tryAcquireShared(arg) < 0)
        doAcquireShared(arg);
}

private void doAcquireShared(int arg) {
	//和上述一樣添加阻塞隊列,只不過是共享標(biāo)識
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            if (p == head) {
          		//和上述一樣排它鎖一樣
                int r = tryAcquireShared(arg);
                if (r >= 0) {
                	//區(qū)別在這 獨(dú)占鎖setHead(node);只是設(shè)置了頭節(jié)點(diǎn),但是共享鎖在獲取到鎖之后,不僅要設(shè)置頭節(jié)點(diǎn),
                	//此時還需要判斷后續(xù)節(jié)點(diǎn)。因?yàn)槭枪蚕淼?,你拿到鎖之后,后續(xù)鏈表中挨著的共享節(jié)點(diǎn)也可以被unpark
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    if (interrupted)
                    	//和獨(dú)占鎖一樣,不帶Interruptibly的方法只是幫你設(shè)置下中斷位
                        selfInterrupt();
                    failed = false;
                    return;
                }
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                //和獨(dú)占鎖一樣,不帶Interruptibly的方法只是幫你設(shè)置下中斷位
                //此時這喚醒之后 是不是又到循環(huán)上面去了,這個節(jié)點(diǎn)就是頭結(jié)點(diǎn)的next節(jié)點(diǎn)
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
 private void setHeadAndPropagate(Node node, int propagate) {
        Node h = head;
        //此時node就是頭節(jié)點(diǎn) 設(shè)置進(jìn)去
        setHead(node);
        //這是共享鎖在獲取到鎖之后的多余的操作,就是順著鏈表傳播下去。為什么呢?舉個場景,共享讀鎖 如果此時下一個節(jié)點(diǎn)還是共享鎖,其實(shí)是需要喚醒的,此情況下是鏈表中的順序 獨(dú)占,共享,共享,共享這種情況
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
            Node s = node.next;
            //
            if (s == null || s.isShared())
            	//如果下一個節(jié)點(diǎn)是共享的就釋放鎖
                doReleaseShared();
        }
    }
}

3.4 doReleaseShared()

for (;;) {
    Node h = head;
    if (h != null && h != tail) {
        int ws = h.waitStatus;
        if (ws == Node.SIGNAL) {
        	//可能有其他線程調(diào)用doReleaseShared(),unpark操作只需要其中一個調(diào)用就行了
        	//正常情況下就是修改成0,unpark改線程,unparkSuccessor上面已描述
            if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                continue;            // loop to recheck cases
            unparkSuccessor(h);
        }
        //如果上述失敗,修改成-3 為了同時release的情況,此時設(shè)想下,如果不改成-3,只變成0 release就下不來了,就會一直hang住 
        //為啥是-3呢 我猜是符合<0的條件吧
        else if (ws == 0 &&
                 !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
            continue;                // loop on failed CAS
    }
    if (h == head)                   // loop if head changed
        break;
}

3.5 releaseShared(int arg)

上面已描述

public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
        doReleaseShared();
        return true;
    }
    return false;
}

3.6 acquireSharedInterruptibly

按照上面的思路很明白的知道會主動拋出中斷異常的獲取鎖的方法

3.7 hasQueuedPredecessors()

public final boolean hasQueuedPredecessors() {
    Node t = tail;
    Node h = head;
    Node s;
    //就是判斷當(dāng)前阻塞隊列里是否有阻塞的線程node
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}

總結(jié)acquireShared:可以資源共享,共享鎖,相對于上述獨(dú)占鎖,在創(chuàng)建Node的時候會標(biāo)記成SHARED,釋放資源喚醒頭結(jié)點(diǎn)的next節(jié)點(diǎn)的時候會喚醒緊挨著的SHARED Node節(jié)點(diǎn)嗎,當(dāng)然是一個喚醒另一個,因?yàn)樵趇f (p == head)的那里循環(huán)產(chǎn)生的。

4 總結(jié)

如類名一樣,通過Queue的Node鏈表來保存阻塞的線程信息,通過state字段的原子操作代表獲取到的資源情況,這個字段是給子類實(shí)現(xiàn)用的。
關(guān)于AQS的ConditionObject詳解請看詳解二
java并發(fā)編程 AbstractQueuedSynchronizer(AQS)詳解二文章來源地址http://www.zghlxwxcb.cn/news/detail-687989.html

到了這里,關(guān)于java并發(fā)編程 AbstractQueuedSynchronizer(AQS)詳解一的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 并發(fā)編程 - AQS 源碼

    1. AQS 源碼 2.?AQS 框架具體實(shí)現(xiàn) - 獨(dú)占鎖實(shí)現(xiàn) ReentrantLock 源碼 實(shí)現(xiàn) ReentrantLock 的三大核心原理: LocksSuport :加鎖解鎖 自旋 :如果沒有加鎖成功就一直自旋 CAS :保證只能有一個線程可以加鎖成功 queue 隊列:用容器保存上面未加鎖成功的阻塞線程,要解鎖的時候從容器中拿出

    2023年04月21日
    瀏覽(20)
  • 并發(fā)編程之深入理解AQS

    并發(fā)編程之深入理解AQS

    目錄 什么是AQS? AQS的特性 AQS總結(jié) 什么是AQS? ? ? ? ?java.util.concurrent包中的大多數(shù)同步器實(shí)現(xiàn)都是圍繞著共同的基礎(chǔ)行為,比如等待隊列、條件隊列、獨(dú)占獲取、共享獲取等,而這些行為的抽象就是基于AbstractQueuedSynchronizer(簡稱AQS)實(shí)現(xiàn)的,AQS是一個抽象同步框架,可以

    2024年01月23日
    瀏覽(31)
  • 10分鐘從源碼級別搞懂AQS(AbstractQueuedSynchronizer)

    10分鐘從源碼級別搞懂AQS(AbstractQueuedSynchronizer)

    上篇文章15000字、6個代碼案例、5個原理圖讓你徹底搞懂Synchronized有說到synchronized由object monitor實(shí)現(xiàn)的 object monitor中由cxq棧和entry list來實(shí)現(xiàn)阻塞隊列,wait set實(shí)現(xiàn)等待隊列,從而實(shí)現(xiàn)synchronized的等待/通知模式 而JDK中的JUC并發(fā)包也通過類似的阻塞隊列和等待隊列實(shí)現(xiàn)等待/通知模

    2024年02月10日
    瀏覽(51)
  • JUC并發(fā)編程之AQS原理

    JUC并發(fā)編程之AQS原理

    全稱是 AbstractQueuedSynchronizer,是阻塞式鎖和相關(guān)的同步器工具的框架 特點(diǎn): 用 state 屬性來表示資源的狀態(tài)(分獨(dú)占模式和共享模式),子類需要定義如何維護(hù)這個生態(tài),控制如何獲取鎖和釋放鎖 getState - 獲取 state 狀態(tài) setState - 設(shè)置 state 狀態(tài) compareAndSetState - cas 機(jī)制設(shè)置 s

    2023年04月18日
    瀏覽(30)
  • java并發(fā)編程 LinkedBlockingDeque詳解

    java 并發(fā)編程系列文章目錄 首先queue是一種數(shù)據(jù)結(jié)構(gòu),一個集合中,先進(jìn)后出,有兩種實(shí)現(xiàn)的方式,數(shù)組和鏈表。從尾部追加,從頭部獲取。Deque是兩端都可以添加,且兩端都可以獲取,所以它的方法會有一系列的Last,F(xiàn)rist語義,添加或獲取等操作會指明哪個方向的,這也是

    2024年02月10日
    瀏覽(21)
  • java并發(fā)編程:ArrayBlockingQueue詳解

    java并發(fā)編程:ArrayBlockingQueue詳解

    ArrayBlockingQueue 顧名思義:基于數(shù)組的阻塞隊列。數(shù)組是要指定長度的,所以使用 ArrayBlockingQueue 時必須指定長度,也就是它是一個有界隊列。它實(shí)現(xiàn)了 BlockingQueue 接口,有著隊列、集合以及阻塞隊列的所有方法。 ArrayBlockingQueue 是線程安全的,內(nèi)部使用 ReentrantLock 來保證。A

    2024年02月08日
    瀏覽(30)
  • java并發(fā)編程:LinkedBlockingQueue詳解

    java并發(fā)編程:LinkedBlockingQueue詳解

    在集合框架里,想必大家都用過ArrayList和LinkedList,也經(jīng)常在面試中問到他們之間的區(qū)別。ArrayList和ArrayBlockingQueue一樣,內(nèi)部基于數(shù)組來存放元素,而LinkedBlockingQueue則和LinkedList一樣,內(nèi)部基于鏈表來存放元素。 LinkedBlockingQueue實(shí)現(xiàn)了BlockingQueue接口,這里放一張類的繼承關(guān)系圖

    2024年02月08日
    瀏覽(64)
  • Java并發(fā)編程詳解:實(shí)現(xiàn)高效并發(fā)應(yīng)用的關(guān)鍵技術(shù)

    在當(dāng)前的計算機(jī)領(lǐng)域,高效的并發(fā)編程對于Java開發(fā)人員而言變得越發(fā)重要。作為流行的編程語言,Java提供了強(qiáng)大的并發(fā)編程支持,使開發(fā)人員能夠充分發(fā)揮多核處理器和線程的潛力,構(gòu)建高性能、高吞吐量的應(yīng)用程序。本文將深入探討Java并發(fā)編程的關(guān)鍵技術(shù),包括線程安全

    2024年02月13日
    瀏覽(30)
  • Java并發(fā)編程之線程池詳解

    Java并發(fā)編程之線程池詳解

    目錄 ??今日良言:不悲傷 不彷徨 有風(fēng)聽風(fēng) 有雨看雨 ??一、簡介 ??二、相關(guān)代碼 ??1.線程池代碼 ??2.自定義實(shí)現(xiàn)線程池 ??三、ThreadPoolExecutor類 首先來介紹一下什么是線程池,線程池是一種利用池化技術(shù)思想來實(shí)現(xiàn)的線程管理技術(shù),主要是為了復(fù)用線程、便利地管理線程

    2024年02月12日
    瀏覽(24)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包