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

Java中的CountDownLatch原理深入解析

 更新時間:2024年01月22日 10:21:28   作者:我不是歐拉_  
這篇文章主要介紹了Java中的CountDownLatch原理深入解析,CountDownLatch是多線程控制的一種同步工具類,它被稱為門閥、 計數(shù)器或者閉鎖,這個工具經(jīng)常用來用來協(xié)調(diào)多個線程之間的同步,或者說起到線程之間的通信,需要的朋友可以參考下

1. CountDownLatch是什么?

CountDownLatch是多線程控制的一種同步工具類,它被稱為門閥、 計數(shù)器或者閉鎖。這個工具經(jīng)常用來用來協(xié)調(diào)多個線程之間的同步,或者說起到線程之間的通信(而不是用作互斥的作用)。

它允許一個或多個線程一直等待,直到其他線程執(zhí)行完后再執(zhí)行。例如,應(yīng)用程序的主線程希望在負責(zé)啟動框架服務(wù)的線程已經(jīng)啟動所有框架服務(wù)之后執(zhí)行。

當(dāng)然利用ReentrantLock + Condition也可以實現(xiàn)線程之間通信,達到同樣的效果

2. 類圖

 可以看出CountDownLatch只有一個內(nèi)部類Sync,Sync繼承AbstractQueuedSynchronizer

3. 實現(xiàn)原理

3.1 示例用法

// N個線程等待主線程
class Driver { // ...
    void main() throws InterruptedException {
        // 開始信號
        CountDownLatch startSignal = new CountDownLatch(1);
        // 完成信號
        CountDownLatch doneSignal = new CountDownLatch(N);
        for (int i = 0; i < N; ++i) // create and start threads
        // 創(chuàng)建N個工作線程并開始運行
        new Thread(new Worker(startSignal, doneSignal)).start();
        // 做準備工作
        doSomethingElse();            // don't let run yet
        // 準備完畢,喚醒工作線程
        startSignal.countDown();      // let all threads proceed
        doSomethingElse();
        // 等待工作線程結(jié)束
        doneSignal.await();           // wait for all to finish
    }
  }
class Worker implements Runnable {
    private final CountDownLatch startSignal;
    private final CountDownLatch doneSignal;
    // 構(gòu)造方法創(chuàng)建工作線程
    Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
        this.startSignal = startSignal;
        this.doneSignal = doneSignal;
    }
    public void run() {
        try {
            // 工作線程進入等待狀態(tài)
            startSignal.await();
            // 工作線程工作
            doWork();
            // 完成工作后,countDown
            doneSignal.countDown();
        } catch (InterruptedException ex) {} // return;
    }
void doWork() { ... }
}
// 主線程等到N個線程
class Driver2 { // ...
    void main() throws InterruptedException {
        // 完成信號
        CountDownLatch doneSignal = new CountDownLatch(N);
        // 創(chuàng)建線程執(zhí)行器
        Executor e = ...
        for (int i = 0; i < N; ++i) // create and start threads
            // 創(chuàng)建并執(zhí)行N個準備工作線程
            e.execute(new WorkerRunnable(doneSignal, i));
            // 主線程等到準備工作線程執(zhí)行完畢
            doneSignal.await();           // wait for all to finish
        }
}
class WorkerRunnable implements Runnable {
    private final CountDownLatch doneSignal;
    private final int i;
    // 構(gòu)造方法
    WorkerRunnable(CountDownLatch doneSignal, int i) {
        this.doneSignal = doneSignal;
        this.i = i;
    }
    // run
    public void run() {
        try {
            // 完成準備工作
            doWork(i);
            // countDown
            doneSignal.countDown();
        } catch (InterruptedException ex) {} // return;
    }
void doWork() { ... }
}

3.2 Sync

private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;
        // State即同步狀態(tài),在不同的實現(xiàn)中叫法不一樣,只是為了方便理解
        // 構(gòu)造方法初始化計數(shù)器計數(shù)值(即同步狀態(tài)值)
        Sync(int count) {
            setState(count);
        }
        // 獲取計數(shù)值
        int getCount() {
            return getState();
        }
        // 共享模式獲取
        protected int tryAcquireShared(int acquires) {
            // 體現(xiàn)出只有計數(shù)值為0時,才能算獲取成功
            return (getState() == 0) ? 1 : -1;
        }
        // 共享模式釋放
        protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();
                // 如果計數(shù)值已經(jīng)為0,直接返回false,結(jié)束自旋
                if (c == 0)
                    return false;
                // 否則計數(shù) - 1
                int nextc = c-1;
                // 通過自旋 + CAS方式改變剩余計數(shù)
                if (compareAndSetState(c, nextc))
                    // 如果計數(shù)為0返回true,否則返回false,結(jié)束自旋
                    // 返回true表示可以喚醒等待的線程
                    return nextc == 0;
            }
        }
    }

通過上面代碼解析可知, CountDownLatch的實現(xiàn)方法都是在內(nèi)部類Sync里面。

3.3 CountDownLatch

public class CountDownLatch {
    // 同步隊列
    private final Sync sync;
    // 構(gòu)造方法初始化計數(shù)值
    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }
    // 線程等待
    public void await() throws InterruptedException {
        // 調(diào)用AQS的acquireSharedInterruptibly方法
        // 即共享模式響應(yīng)中斷的獲取
        sync.acquireSharedInterruptibly(1);
    }
    // 計數(shù) - 1
    public void countDown() {
        sync.releaseShared(1);
    }
}

3.3.1 await() 方法解析

// CountDownLatch
public void await() throws InterruptedException {
        // 調(diào)用AQS的acquireSharedInterruptibly方法
        sync.acquireSharedInterruptibly(1);
    }
// 進入AQS
public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        // 中斷判斷
        if (Thread.interrupted())
            throw new InterruptedException();
        // 如果沒有獲取到同步狀態(tài),或者說計數(shù)值不為0
        // 則調(diào)用doAcquireSharedInterruptibly方法,進入同步隊列
        // 如果計數(shù)值為0則執(zhí)行后續(xù)業(yè)務(wù)邏輯
        if (tryAcquireShared(arg) < 0)
            // 該方法的解析參考文章結(jié)尾的鏈接,此處不再贅述
            doAcquireSharedInterruptibly(arg);
    }
// CountDownLatch 中tryAcquireShared的實現(xiàn)
protected int tryAcquireShared(int acquires) {
            // 當(dāng)計數(shù)為0時,線程才不會進入同步隊列
            return (getState() == 0) ? 1 : -1;
        }

通過上面代碼可以知道,如果計數(shù)值為0,表示獲取成功。這就是CountDownLatch的機制,嘗試獲取latch的線程只有當(dāng)latch的值減到0的時候,才能獲取成功。

3.3.2 countDown() 方法解析

// CountDownLatch
public void countDown() {
        // 調(diào)用AQS的releaseShared方法
        sync.releaseShared(1);
    }
// 進入AQS
public final boolean releaseShared(int arg) {
        // 共享模式釋放
        if (tryReleaseShared(arg)) {
            // 如果釋放成功則喚醒等待的線程,并返回true
            // 具體喚醒邏輯不再贅述,參考AQS解析文章
            doReleaseShared();
            return true;
        }
        return false;
    }
// CountDownLatch 中tryReleaseShared的實現(xiàn)
protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                 // 通過自旋 + CAS方式改變剩余計數(shù)
                if (compareAndSetState(c, nextc))
                    // 如果計數(shù)為0返回true,否則返回false,結(jié)束自旋
                    // 返回true表示可以喚醒等待的線程
                    return nextc == 0;
            }
        }

3.3.3 CountDownLatch如何喚醒所有調(diào)用 await() 等待的線程呢?

當(dāng)調(diào)用doReleaseShared()喚醒后繼節(jié)點后,回到線程被掛起的地方,也就是doAcquireSharedInterruptibly(int arg)方法中

    private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        // 將當(dāng)前線程加入同步隊列的尾部
        final Node node = addWaiter(Node.SHARED);
        try {
            // 自旋
            for (;;) {
                // 獲取當(dāng)前節(jié)點的前驅(qū)節(jié)點
                final Node p = node.predecessor();
                // 如果前驅(qū)節(jié)點是頭結(jié)點,則嘗試獲取同步狀態(tài)
                if (p == head) {
                    // 當(dāng)前節(jié)點嘗試獲取同步狀態(tài)
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        // 如果獲取成功,則設(shè)置當(dāng)前節(jié)點為頭結(jié)點
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        return;
                    }
                }
                // 如果當(dāng)前節(jié)點的前驅(qū)不是頭結(jié)點,嘗試掛起當(dāng)前線程
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } catch (Throwable t) {
            cancelAcquire(node);
            throw t;
        }
    }

當(dāng)頭結(jié)點的后繼節(jié)點被喚醒后,線程將從掛起的地方醒來,繼續(xù)執(zhí)行,因為沒有return,所以進入下一次循環(huán)。

此時,獲取同步狀態(tài)成功,執(zhí)行setHeadAndPropagate(node, r)。

    // 如果執(zhí)行這個函數(shù),那么propagate一定等于1
    private void setHeadAndPropagate(Node node, int propagate) {
        // 獲取頭結(jié)點
        Node h = head;
        // 因為當(dāng)前節(jié)點被喚醒,設(shè)置當(dāng)前節(jié)點為頭結(jié)點
        setHead(node);
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
            // 獲取當(dāng)前節(jié)點的下一個節(jié)點
            Node s = node.next;
            // 如果下一個節(jié)點為null或者節(jié)點為shared節(jié)點
            if (s == null || s.isShared())
                doReleaseShared();
        }
    }
    private void doReleaseShared() {
    // 自旋
    for (;;) {
        Node h = head;
        // 如果隊列存在排隊的節(jié)點
        if (h != null && h != tail) {
            int ws = h.waitStatus;
            if (ws == Node.SIGNAL) {
                // CAS設(shè)置不成功則不斷循環(huán)
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                    continue;            // loop to recheck cases
                // CAS操作成功后釋放后繼節(jié)點,并喚醒線程
                unparkSuccessor(h);
            }
            else if (ws == 0 &&
                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                continue;                // loop on failed CAS
        }
        // 隊列不存在排隊的節(jié)點,直接結(jié)束自旋
        if (h == head)                   // loop if head changed
            break;
    }
 

調(diào)用doReleaseShared方法喚醒后繼節(jié)點,后繼節(jié)點又回到線程被掛起的地方,也就是doAcquireSharedInterruptibly(int arg)方法中,實現(xiàn)循環(huán)喚醒所有await的線程。

此篇文章只解析了CountDownLatch的實現(xiàn),它就是一個基于 AQS 的計數(shù)器,它內(nèi)部的方法都是圍繞 AQS 框架來實現(xiàn)的。

建議感興趣的同學(xué)先去了解AQS原理,只要明白了AQS的實現(xiàn)原理,再來看CountDownLatch、Semaphore、ReentrantLock等實現(xiàn)原理就一目了然了。

到此這篇關(guān)于Java中的CountDownLatch原理深入解析的文章就介紹到這了,更多相關(guān)CountDownLatch原理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java實現(xiàn)SHA1加密代碼實例

    Java實現(xiàn)SHA1加密代碼實例

    這篇文章給大家分享了Java實現(xiàn)SHA1加密的相關(guān)實例代碼,有興趣的朋友可以測試參考下。
    2018-07-07
  • idea開啟熱部署Devtools的步驟詳解

    idea開啟熱部署Devtools的步驟詳解

    當(dāng)我們在 idea 中修改代碼的時候,idea 并不會自動的重啟去響應(yīng)我們修改的內(nèi)容,而是需要我們手動的重新啟動項目才可以生效,這個是非常不方便,但是可以在 idea 中開啟這個自動熱部署的功能,本文給大家介紹了idea開啟熱部署Devtools的步驟,需要的朋友可以參考下
    2024-03-03
  • Java分支循環(huán)與數(shù)組核心知識點總結(jié)

    Java分支循環(huán)與數(shù)組核心知識點總結(jié)

    這篇文章主要介紹了Java分支循環(huán)與數(shù)組核心知識點總結(jié)的相關(guān)資料,理解循環(huán)與分支結(jié)構(gòu)是掌握編程的基礎(chǔ),重復(fù)執(zhí)行操作的循環(huán)和基于條件的執(zhí)行路徑分支有助于構(gòu)建復(fù)雜的程序邏輯,需要的朋友可以參考下
    2026-01-01
  • java.util.Collection源碼分析與深度理解

    java.util.Collection源碼分析與深度理解

    這篇文章主要給大家介紹了關(guān)于java.util.Collection的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • Java中線程Thread的特點及使用

    Java中線程Thread的特點及使用

    這篇文章主要介紹了Java中線程的特點及使用,線程是進程的組成部分,一個進程可以擁有多個線程,而一個線程必須擁有一個父進程,那么線程該如何使用,讓我們一起來看看吧
    2023-04-04
  • 解決SpringBoot引用別的模塊無法注入的問題

    解決SpringBoot引用別的模塊無法注入的問題

    這篇文章主要介紹了解決SpringBoot引用別的模塊無法注入的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • Java喚醒本地應(yīng)用的兩種方法詳解

    Java喚醒本地應(yīng)用的兩種方法詳解

    這篇文章主要為大家介紹了Java喚醒本地應(yīng)用的兩種方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08
  • SpringBoot內(nèi)存數(shù)據(jù)導(dǎo)出成Excel的實現(xiàn)方法

    SpringBoot內(nèi)存數(shù)據(jù)導(dǎo)出成Excel的實現(xiàn)方法

    這篇文章主要給大家介紹了關(guān)于SpringBoot內(nèi)存數(shù)據(jù)導(dǎo)出成Excel的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • java字符串中提取數(shù)字的方法

    java字符串中提取數(shù)字的方法

    Java中提取字符串中的數(shù)字,可以使用正則表達式或非正則表達式的方法,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2023-06-06
  • 實例分析java對象的序列化和反序列化

    實例分析java對象的序列化和反序列化

    序列化 (Serialization)是將對象的狀態(tài)信息轉(zhuǎn)換為可以存儲或傳輸?shù)男问降倪^程。一般將一個對象存儲至一個儲存媒介,例如檔案或是記億體緩沖等。在網(wǎng)絡(luò)傳輸過程中,可以是字節(jié)或是XML等格式。而字節(jié)的或XML編碼格式可以還原完全相等的對象。這個相反的過程又稱為反序列化
    2018-09-09

最新評論

施秉县| 双峰县| 太仓市| 岳普湖县| 濮阳市| 凤阳县| 山东| 息烽县| 城固县| 准格尔旗| 凤庆县| 林西县| 宁乡县| 桐梓县| 屏东县| 麟游县| 安国市| 台中县| 嘉义市| 灵武市| 郁南县| 大关县| 建水县| 凤山市| 儋州市| 平邑县| 贵溪市| 巴青县| 仙桃市| 教育| 陆川县| 财经| 昌吉市| 新野县| 玉田县| 麟游县| 康平县| 石首市| 甘谷县| 布尔津县| 乐至县|