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

Java多線程中的CountDownLatch解析

 更新時(shí)間:2023年11月16日 09:38:54   作者:進(jìn)擊的貓  
這篇文章主要介紹了Java多線程中的CountDownLatch解析,CountDownLatch是一個(gè)阻塞部分線程直到其他線程執(zhí)行完成后喚醒的同步計(jì)數(shù)器,核心是其內(nèi)部類Sync繼承于AQS,同時(shí)也是利用的AQS的同步原理,也稱之為閉鎖,需要的朋友可以參考下

一、概念簡(jiǎn)介

CountDownLatch是一個(gè)阻塞部分線程直到其他線程執(zhí)行完成后喚醒的同步計(jì)數(shù)器

核心是其內(nèi)部類Sync繼承于AQS,同時(shí)也是利用的AQS的同步原理,也稱之為閉鎖。

二、使用場(chǎng)景

當(dāng)主線程進(jìn)行執(zhí)行時(shí),利用構(gòu)造方法初始化一個(gè)同步數(shù)state(AQS原理),主線程調(diào)用await方法進(jìn)行阻塞主線程即誰(shuí)調(diào)用誰(shuí)阻塞,其它線程調(diào)用countDown方法會(huì)對(duì)計(jì)數(shù)器減1直到0,會(huì)精準(zhǔn)喚醒被阻塞線程即被await方法阻塞的線程。

(1)用于多種數(shù)據(jù)源數(shù)據(jù)匯總;

(2)等待某一時(shí)間點(diǎn)才執(zhí)行邏輯如加載緩存、加載配置等;

注意:為了程序的健壯性,盡量給出合適的時(shí)間,防止子線程中斷導(dǎo)致線程無(wú)法喚醒的情況發(fā)生。

三、特點(diǎn)

(1)子線程調(diào)用countDown方法只會(huì)減1,不會(huì)阻塞線程;

(2)主線程調(diào)用await方法會(huì)導(dǎo)致其被阻塞,當(dāng)計(jì)數(shù)器state被其他線程調(diào)用countDown方法減至0會(huì)喚醒被阻塞的線程;

(3)當(dāng)主線程發(fā)生中斷會(huì)拋出異常,導(dǎo)致無(wú)法喚醒主線程即無(wú)法達(dá)到屏障點(diǎn)。

CountDownLatch簡(jiǎn)單使用

 public static void main(String[] args) {
        System.out.println("main 線程開始執(zhí)行!");
        CountDownLatch latch = new CountDownLatch(5);//初始化同步數(shù)
        for (int i = 0; i < 5; i++) {
            int threadId = i+1;
            new Thread(()->{
                System.out.println("線程"+threadId+"執(zhí)行!");
                latch.countDown();
            }).start();//java8 lamda表達(dá)式
        }
        System.out.println("即將被阻塞!");
        try {
            latch.await();//阻塞主線程,等待子線程將state減至0被喚醒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("main 線程執(zhí)行完畢!");
    }

四、CountDownLatch源碼分析

(1)構(gòu)造函數(shù)

 /**
  * CountDownLatch唯一的構(gòu)造函數(shù),實(shí)例化時(shí)只能使用指定同步數(shù)的構(gòu)造方法
  */
  public CountDownLatch(int count) {
      if (count < 0) throw new IllegalArgumentException("count < 0");
      this.sync = new Sync(count);//利用內(nèi)部類(繼承AQS)對(duì)state進(jìn)行設(shè)置初始化大小
  }

(2)await方法(核心)

CountDownLatch類:

public void await() throws InterruptedException {
    //核心成員變量sync調(diào)用AQS中的方法acquireSharedInterruptibly
    sync.acquireSharedInterruptibly(1);
}

AQS類:

  public final void acquireSharedInterruptibly(int arg)
              throws InterruptedException {
      if (Thread.interrupted())//判斷是否有中斷標(biāo)志
          throw new InterruptedException();
          /**
          * 該方法是由子類重寫,AQS強(qiáng)制其子類重寫,否則報(bào)錯(cuò)
          * 根據(jù)if中的值判斷是否需要阻塞操作 1代表不需要阻塞 -1代表需要阻塞
          */
      if (tryAcquireShared(arg) < 0)
          doAcquireSharedInterruptibly(arg);//調(diào)用AQS共享鎖阻塞操作
  }

Sync類:

/**
 * 獲取同步數(shù)并判斷是否需要喚醒
 * 同步數(shù)state為0,則需要喚醒返回1即不需要阻塞
 * 同步數(shù)state不為1,則不需要喚醒,返回-1后的操作即阻塞
 */
 protected int tryAcquireShared(int acquires) {
     return (getState() == 0) ? 1 : -1;//獲取AQS中的state進(jìn)行返回是否需要進(jìn)行阻塞操作
 }
//以共享鎖的方式進(jìn)行阻塞
private void doAcquireSharedInterruptibly(int arg)
    throws InterruptedException {
    /**
    * addWaiter方法主要是基于當(dāng)前線程創(chuàng)建一個(gè)等待著并入隊(duì)且會(huì)創(chuàng)建一個(gè)哨兵節(jié)點(diǎn)
    * addWaiter具體細(xì)節(jié)和其內(nèi)部enq初始化隊(duì)列方法請(qǐng)轉(zhuǎn)入AQS分析
    */
    final Node node = addWaiter(Node.SHARED);//以共享鎖創(chuàng)建一個(gè)等待者node
    boolean failed = true;
    try {
        for (;;) {//自旋,是否需要阻塞
            final Node p = node.predecessor();//當(dāng)前線程的前繼節(jié)點(diǎn)
            if (p == head) {//前繼節(jié)點(diǎn)是否為頭節(jié)點(diǎn)
                int r = tryAcquireShared(arg);//嘗試獲取共享鎖即是否需要阻塞1和-1值
                if (r >= 0) {//當(dāng)其大于等于時(shí),r值只能時(shí)1或者-1,滿足該條件時(shí)則說(shuō)明不需要阻塞
                    setHeadAndPropagate(node, r);//設(shè)置新的頭結(jié)點(diǎn)并釋放共享鎖
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
            }
            /**
            * shouldParkAfterFailedAcquire主要是改變前節(jié)點(diǎn)的等待信號(hào)量
            * parkAndCheckInterrupt在前者返回TRUE的情況下會(huì)直接調(diào)用LockSupport.park()進(jìn)行阻塞
            * 上述兩種方法在AQS分析中可找到詳細(xì)解釋
            */
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                throw new InterruptedException();//上述兩個(gè)條件滿足則代表線程被中斷過(guò)
        }
    } finally {
        if (failed)//出現(xiàn)異常且未執(zhí)行for循環(huán)中改變?cè)揻ailed值
            cancelAcquire(node);//取消超時(shí)節(jié)點(diǎn)和當(dāng)前節(jié)點(diǎn)取消喚醒,AQS原理分析中詳細(xì)講解
    }
}

(3)countDown方法(核心)

//用于子線程調(diào)用將同步數(shù)-1
public void countDown() {
    sync.releaseShared(1);//通過(guò)內(nèi)部成員變量sync調(diào)用內(nèi)部Sync類繼承AQS中的釋放方法
}

AQS類:

public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {//AQS類中定義強(qiáng)制子類重寫該方法,用于是否需要喚醒被阻塞的線程
        doReleaseShared();//滿足判斷條件則進(jìn)行正常釋放
        return true;//釋放成功
    }
    return false;//不需要釋放
}

Sync類:

/**
* 主要利用自旋鎖的原理,對(duì)state值進(jìn)行-1
*/
protected boolean tryReleaseShared(int releases) {
    for (;;) {
        int c = getState();//獲取state值
        if (c == 0)//還未開始自減,已為0則代表不能正常釋放
            return false;
        int nextc = c-1;
        if (compareAndSetState(c, nextc))//CAS對(duì)state值進(jìn)行設(shè)置新的值
            return nextc == 0;//計(jì)數(shù)器是否為0,此狀態(tài)為0代表可以正常釋放
    }
}
/**
 * 釋放共享鎖
 */
private void doReleaseShared() {
    for (;;) {//自旋
        Node h = head;//頭節(jié)點(diǎn)
        if (h != null && h != tail) {//代表可喚醒且不是尾結(jié)點(diǎn)
            int ws = h.waitStatus;
            if (ws == Node.SIGNAL) {//頭節(jié)點(diǎn)的等待狀態(tài)為喚醒信號(hào)量
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                    continue;            // loop to recheck cases
                unparkSuccessor(h);//if中cas操作成功,則執(zhí)行該喚醒方法,否則進(jìn)行自旋或者結(jié)束
            }else if (ws == 0 &&//初始化但未被改變時(shí)
                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))//設(shè)置為無(wú)條件喚醒
                continue;// loop on failed CAS 該else if中CAS失敗進(jìn)行自旋
        }
        if (h == head)//loop if head changed 循環(huán)判定頭結(jié)點(diǎn)是否發(fā)生變化,實(shí)際上是喚醒后會(huì)執(zhí)行這里結(jié)束自旋
            break;
    }
}

AQS喚醒共享鎖

/**
* (1)對(duì)信號(hào)量節(jié)點(diǎn)即前繼節(jié)點(diǎn)等待值還原
* (2)對(duì)于node節(jié)點(diǎn)的后繼節(jié)點(diǎn)不為null直接喚醒或從后往前找尋信號(hào)量最靠前的線程進(jìn)行喚醒
*/
private void unparkSuccessor(Node node) {
    int ws = node.waitStatus;//該節(jié)點(diǎn)等待狀態(tài)即頭結(jié)點(diǎn)的信號(hào)量
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);//將該節(jié)點(diǎn)的狀態(tài)值設(shè)置為0即初始值
    Node s = node.next;//獲取喚醒節(jié)點(diǎn)即node的下一節(jié)點(diǎn)
    if (s == null || s.waitStatus > 0) {
        s = null;
        for (Node t = tail; t != null && t != node; t = t.prev)//從后往前查找最靠前的信號(hào)量node
            if (t.waitStatus <= 0)//信號(hào)量或初始化值
                s = t;
    }
    if (s != null)//找到喚醒節(jié)點(diǎn)
        LockSupport.unpark(s.thread);對(duì)該線程進(jìn)行喚醒
}

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

相關(guān)文章

  • SpringBoot集成flink全過(guò)程

    SpringBoot集成flink全過(guò)程

    文章介紹了Flink作為批處理和流處理結(jié)合的統(tǒng)一計(jì)算框架,特別是其強(qiáng)大的流處理能力,文章還詳細(xì)描述了如何在本地和集群環(huán)境中搭建Flink,并通過(guò)Netcat工具生成一個(gè)無(wú)界流測(cè)試,文章最后提供了啟動(dòng)socket流的步驟和示例代碼,希望對(duì)讀者有所幫助
    2025-01-01
  • SpringBoot校驗(yàn)前端傳遞的參數(shù)的流程步驟

    SpringBoot校驗(yàn)前端傳遞的參數(shù)的流程步驟

    在日常項(xiàng)目開發(fā)中,我們都知道參數(shù)驗(yàn)證是必不可少的一環(huán),但是有時(shí)候?yàn)榱送祽?把參數(shù)校驗(yàn)交給前端開發(fā)人員去處理,這樣很容易影響系統(tǒng)穩(wěn)定性和安全性,所以本文給大家介紹了SpringBoot校驗(yàn)前端傳遞的參數(shù)的流程步驟,需要的朋友可以參考下
    2025-10-10
  • Java中注解@JsonFormat的用法詳解

    Java中注解@JsonFormat的用法詳解

    這篇文章主要給大家介紹了關(guān)于Java中注解@JsonFormat用法的相關(guān)資料,以及分享了@JsonFormat 將枚舉序列化為對(duì)象的方法,文中給出了詳細(xì)的代碼實(shí)例,需要的朋友可以參考下
    2023-01-01
  • 詳解Java環(huán)境變量配置方法(Windows)

    詳解Java環(huán)境變量配置方法(Windows)

    這篇文章主要介紹了Java環(huán)境變量配置方法(Windows),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • 輕松掌握java組合模式

    輕松掌握java組合模式

    這篇文章主要幫助大家輕松掌握java組合模式,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-09-09
  • Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(24)

    Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(24)

    下面小編就為大家?guī)?lái)一篇Java基礎(chǔ)的幾道練習(xí)題(分享)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧,希望可以幫到你
    2021-07-07
  • 使用Java獲取系統(tǒng)信息的常用代碼整理總結(jié)

    使用Java獲取系統(tǒng)信息的常用代碼整理總結(jié)

    這篇文章主要介紹了使用Java獲取系統(tǒng)信息的常用代碼整理總結(jié),在服務(wù)器端一般經(jīng)常能夠用到,歡迎收藏,需要的朋友可以參考下
    2015-11-11
  • Java 高并發(fā)八:NIO和AIO詳解

    Java 高并發(fā)八:NIO和AIO詳解

    本文主要介紹Java 高并發(fā)NIO和AIO 的知識(shí),這里整理了詳細(xì)的資料,并詳細(xì)介紹了 1. 什么是NIO 2. Buffer 3. Channel 4. 網(wǎng)絡(luò)編程 5. AIO的知識(shí),有需要的小伙伴可以參考下
    2016-09-09
  • Java字符串操作和C#字符串操作的不同小結(jié)

    Java字符串操作和C#字符串操作的不同小結(jié)

    在JAVA語(yǔ)言中,字符串?dāng)?shù)據(jù)實(shí)際上由String類所實(shí)現(xiàn)的。下面這篇文章主要給大家介紹了關(guān)于Java字符串操作和C#字符串操作的不同的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2018-12-12
  • Spring Boot實(shí)現(xiàn)文件上傳示例代碼

    Spring Boot實(shí)現(xiàn)文件上傳示例代碼

    本篇文章主要介紹了Spring Boot實(shí)現(xiàn)文件上傳示例代碼,可以實(shí)現(xiàn)單文件和多文件的上傳,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。
    2017-03-03

最新評(píng)論

大丰市| 镇江市| 南宁市| 察哈| 康保县| 扎鲁特旗| 诸暨市| 临高县| 荆门市| 华阴市| 泰兴市| 江川县| 安福县| 中山市| 罗田县| 桂东县| 花垣县| 鲁山县| 石门县| 望谟县| 兰州市| 灵寿县| 互助| 淳安县| 潜山县| 平江县| 南充市| 屯昌县| 新余市| 海林市| 额尔古纳市| 西安市| 惠州市| 淮北市| 津南区| 石阡县| 百色市| 临西县| 凤阳县| 都匀市| 安丘市|