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

Java多線程中的CountDownLatch解析

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

一、概念簡介

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

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

二、使用場景

當主線程進行執(zhí)行時,利用構造方法初始化一個同步數(shù)state(AQS原理),主線程調用await方法進行阻塞主線程即誰調用誰阻塞,其它線程調用countDown方法會對計數(shù)器減1直到0,會精準喚醒被阻塞線程即被await方法阻塞的線程。

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

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

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

三、特點

(1)子線程調用countDown方法只會減1,不會阻塞線程;

(2)主線程調用await方法會導致其被阻塞,當計數(shù)器state被其他線程調用countDown方法減至0會喚醒被阻塞的線程;

(3)當主線程發(fā)生中斷會拋出異常,導致無法喚醒主線程即無法達到屏障點。

CountDownLatch簡單使用

 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表達式
        }
        System.out.println("即將被阻塞!");
        try {
            latch.await();//阻塞主線程,等待子線程將state減至0被喚醒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("main 線程執(zhí)行完畢!");
    }

四、CountDownLatch源碼分析

(1)構造函數(shù)

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

(2)await方法(核心)

CountDownLatch類:

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

AQS類:

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

Sync類:

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

(3)countDown方法(核心)

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

AQS類:

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

Sync類:

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

AQS喚醒共享鎖

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

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

相關文章

  • SpringBoot集成flink全過程

    SpringBoot集成flink全過程

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

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

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

    Java中注解@JsonFormat的用法詳解

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

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

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

    輕松掌握java組合模式

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

    Java日常練習題,每天進步一點點(24)

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

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

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

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

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

    Java字符串操作和C#字符串操作的不同小結

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

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

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

最新評論

赣榆县| 阿勒泰市| 沂南县| 海淀区| 瑞安市| 建昌县| 九台市| 高碑店市| 灯塔市| 启东市| 和龙市| 龙游县| 合肥市| 利辛县| 亳州市| 井陉县| 耿马| 凯里市| 双峰县| 新沂市| 西乌珠穆沁旗| 元谋县| 邹平县| 蛟河市| 镇安县| 筠连县| 长丰县| 金塔县| 抚州市| 顺义区| 彭泽县| 双江| 鄂州市| 河津市| 贵德县| 越西县| 余姚市| 天等县| 靖州| 金华市| 雅安市|