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

Java多線程中的CountDownLatch解析

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

一、概念簡介

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

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

二、使用場景

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

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

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

注意:為了程序的健壯性,盡量給出合適的時(shí)間,防止子線程中斷導(dǎo)致線程無法喚醒的情況發(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)致無法喚醒主線程即無法達(dá)到屏障點(diǎn)。

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表達(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)對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ì)列方法請轉(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í)則說明不需要阻塞
                    setHeadAndPropagate(node, r);//設(shè)置新的頭結(jié)點(diǎn)并釋放共享鎖
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
            }
            /**
            * shouldParkAfterFailedAcquire主要是改變前節(jié)點(diǎn)的等待信號量
            * parkAndCheckInterrupt在前者返回TRUE的情況下會(huì)直接調(diào)用LockSupport.park()進(jìn)行阻塞
            * 上述兩種方法在AQS分析中可找到詳細(xì)解釋
            */
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                throw new InterruptedException();//上述兩個(gè)條件滿足則代表線程被中斷過
        }
    } finally {
        if (failed)//出現(xiàn)異常且未執(zhí)行for循環(huán)中改變該failed值
            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);//通過內(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類:

/**
* 主要利用自旋鎖的原理,對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對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)為喚醒信號量
                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è)置為無條件喚醒
                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)對信號量節(jié)點(diǎn)即前繼節(jié)點(diǎn)等待值還原
* (2)對于node節(jié)點(diǎn)的后繼節(jié)點(diǎn)不為null直接喚醒或從后往前找尋信號量最靠前的線程進(jìn)行喚醒
*/
private void unparkSuccessor(Node node) {
    int ws = node.waitStatus;//該節(jié)點(diǎn)等待狀態(tài)即頭結(jié)點(diǎn)的信號量
    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)//從后往前查找最靠前的信號量node
            if (t.waitStatus <= 0)//信號量或初始化值
                s = t;
    }
    if (s != null)//找到喚醒節(jié)點(diǎn)
        LockSupport.unpark(s.thread);對該線程進(jìn)行喚醒
}

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

相關(guān)文章

  • 詳解Java如何優(yōu)雅的處理異常

    詳解Java如何優(yōu)雅的處理異常

    在編寫 Java 程序的過程中,有一種異常幾乎每個(gè)開發(fā)者都會(huì)遇到——空指針異常(NullPointerException),那么我們應(yīng)該如何有效且優(yōu)雅的處理空指針異常呢,下面小編就來詳細(xì)介紹這個(gè)處理方案吧
    2023-08-08
  • Java基于API接口爬取商品數(shù)據(jù)的示例代碼

    Java基于API接口爬取商品數(shù)據(jù)的示例代碼

    Java作為一種流行的編程語言,可以用于編寫程序來調(diào)用這些API接口,從而獲取商品數(shù)據(jù),本文將介紹如何使用Java基于API接口爬取商品數(shù)據(jù),包括請求API、解析JSON數(shù)據(jù)、存儲數(shù)據(jù)等步驟,并提供相應(yīng)的代碼示例,感興趣的朋友跟隨小編一起看看吧
    2023-10-10
  • SpringBoot定時(shí)任務(wù)詳解與案例代碼

    SpringBoot定時(shí)任務(wù)詳解與案例代碼

    SpringBoot是一個(gè)流行的Java開發(fā)框架,它提供了許多便捷的特性來簡化開發(fā)過程,其中之一就是定時(shí)任務(wù)的支持,讓開發(fā)人員可以輕松地在應(yīng)用程序中執(zhí)行定時(shí)任務(wù),本文將詳細(xì)介紹如何在Spring?Boot中使用定時(shí)任務(wù),并提供相關(guān)的代碼示例
    2023-06-06
  • Java class文件格式之方法_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    Java class文件格式之方法_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    這篇文章主要為大家詳細(xì)介紹了Java class文件格式之方法的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • Spring AOP使用之多切面運(yùn)行順序

    Spring AOP使用之多切面運(yùn)行順序

    這篇文章主要介紹了Spring AOP使用之多切面運(yùn)行順序,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • java 中模擬UDP傳輸?shù)陌l(fā)送端和接收端實(shí)例詳解

    java 中模擬UDP傳輸?shù)陌l(fā)送端和接收端實(shí)例詳解

    這篇文章主要介紹了java 中模擬UDP傳輸?shù)陌l(fā)送端和接收端實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下
    2017-03-03
  • Jedis出現(xiàn)connection timeout問題解決方法(JedisPool連接池使用實(shí)例)

    Jedis出現(xiàn)connection timeout問題解決方法(JedisPool連接池使用實(shí)例)

    這篇文章主要介紹了Jedis出現(xiàn)connection timeout問題解決方法,使用Jedis的JedisPool連接池解決了這個(gè)問題,需要的朋友可以參考下
    2014-05-05
  • spring.factories文件的解析源碼API機(jī)制詳解

    spring.factories文件的解析源碼API機(jī)制詳解

    通過本文深入探討Spring?Boot的背景歷史、業(yè)務(wù)場景、功能點(diǎn)以及底層原理,使讀者對Spring?Boot有了更深入的了解,結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
    2024-11-11
  • 解析Java編程中對于包結(jié)構(gòu)的命名和訪問

    解析Java編程中對于包結(jié)構(gòu)的命名和訪問

    這篇文章主要介紹了Java編程中對于包結(jié)構(gòu)的命名和訪問,是Java入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下
    2015-12-12
  • Java?spring注解@PostConstruct實(shí)戰(zhàn)案例講解

    Java?spring注解@PostConstruct實(shí)戰(zhàn)案例講解

    我們在Spring項(xiàng)目中經(jīng)常會(huì)遇到@PostConstruct注解,可能有的伙伴對這個(gè)注解很陌生,下面這篇文章主要給大家介紹了關(guān)于Java?spring注解@PostConstruct實(shí)戰(zhàn)案例講解的相關(guān)資料,需要的朋友可以參考下
    2023-12-12

最新評論