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

Java AQS中閉鎖CountDownLatch的使用

 更新時(shí)間:2023年02月02日 09:33:55   作者:飛奔的小付  
CountDownLatch 是一個(gè)同步工具類,用來協(xié)調(diào)多個(gè)線程之間的同步,它能夠使一個(gè)線程在等待另外一些線程完成各自工作之后,再繼續(xù)執(zhí)行。被將利用CountDownLatch實(shí)現(xiàn)網(wǎng)絡(luò)同步請(qǐng)求,異步同時(shí)獲取商品信息組裝,感興趣的可以了解一下

一. 簡(jiǎn)介

CountDownLatch(閉鎖)是一個(gè)同步協(xié)助類,允許一個(gè)或多個(gè)線程等待,直到其他線程完成操作集。

CountDownLatch使用給定的計(jì)數(shù)值(count)初始化。await方法會(huì)阻塞直到當(dāng)前的計(jì)數(shù)值(count)由于countDown方法的調(diào)用達(dá)到0,count為0之后所有等待的線程都會(huì)被釋放,并且隨后對(duì)await方法的調(diào)用都會(huì)立即返回。這是一個(gè)一次性現(xiàn)象 —— count不會(huì)被重置。如果你需要一個(gè)重置count的版本,那么請(qǐng)考慮使用CyclicBarrier。

二. 使用

構(gòu)造器

 public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }

常用方法

 // 調(diào)用 await() 方法的線程會(huì)被掛起,它會(huì)等待直到 count 值為 0 才繼續(xù)執(zhí)行
public void await() throws InterruptedException { };  
// 和 await() 類似,若等待 timeout 時(shí)長(zhǎng)后,count 值還是沒有變?yōu)?0,不再等待,繼續(xù)執(zhí)行
public boolean await(long timeout, TimeUnit unit) throws InterruptedException { };  
// 會(huì)將 count 減 1,直至為 0
public void countDown() {
        sync.releaseShared(1);
    }

三. 應(yīng)用場(chǎng)景

CountDownLatch一般用作多線程倒計(jì)時(shí)計(jì)數(shù)器,強(qiáng)制它們等待其他一組(CountDownLatch的初始化決定)任務(wù)執(zhí)行完成。

CountDownLatch的兩種使用場(chǎng)景:

  • 讓多個(gè)線程等待
  • 讓單個(gè)線程等待

場(chǎng)景1 讓多個(gè)線程等待:模擬并發(fā),讓并發(fā)線程一起執(zhí)行

 public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(1);
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                try {
                   //等待
                    countDownLatch.await();
                    String parter = "【" + Thread.currentThread().getName() + "】";
                    System.out.println(parter + "開始執(zhí)行……");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
        Thread.sleep(2000);
        countDownLatch.countDown();
    }

for循環(huán)中等待阻塞,直到執(zhí)行countdown方法。

場(chǎng)景2 讓單個(gè)線程等待:多個(gè)線程(任務(wù))完成后,進(jìn)行匯總合并。

很多時(shí)候,我們的并發(fā)任務(wù),存在前后依賴關(guān)系;比如數(shù)據(jù)詳情頁(yè)需要同時(shí)調(diào)用多個(gè)接口獲取數(shù)據(jù),并發(fā)請(qǐng)求獲取到數(shù)據(jù)后、需要進(jìn)行結(jié)果合并;或者多個(gè)數(shù)據(jù)操作完成后,需要數(shù)據(jù)check;這其實(shí)都是:在多個(gè)線程(任務(wù))完成后,進(jìn)行匯總合并的場(chǎng)景。

   public static void main(String[] args) throws Exception {
        CountDownLatch countDownLatch = new CountDownLatch(5);
        for (int i = 0; i < 5; i++) {
            final int index = i;
            new Thread(() -> {
                try {
                    Thread.sleep(1000 +
                            ThreadLocalRandom.current().nextInt(1000));
                    System.out.println(Thread.currentThread().getName()
                            + " finish task" + index);

                    countDownLatch.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
        // 主線程在阻塞,當(dāng)計(jì)數(shù)器==0,就喚醒主線程往下執(zhí)行。
        countDownLatch.await();
        System.out.println("主線程:在所有任務(wù)運(yùn)行完成后,進(jìn)行結(jié)果匯總");
    }

四. 底層原理

底層基于 AbstractQueuedSynchronizer 實(shí)現(xiàn),CountDownLatch 構(gòu)造函數(shù)中指定的count直接賦給AQS的state;每次countDown()則都是release(1)減1,最后減到0時(shí)unpark線程;這一步是由最后一個(gè)執(zhí)行countdown方法的線程執(zhí)行的。

而調(diào)用await()方法時(shí),當(dāng)前線程就會(huì)判斷state屬性是否為0,如果為0,則繼續(xù)往下執(zhí)行,如果不為0,則使當(dāng)前線程進(jìn)入等待狀態(tài),直到某個(gè)線程將state屬性置為0,其就會(huì)喚醒在await()方法中等待的線程。

構(gòu)造方法

 public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }
	    Sync(int count) {
            setState(count);
        }
 protected final void setState(int newState) {
        state = newState;
    }

阻塞

public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }
 public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
            //arg為1,不為0 ,返回-1,這里小于0
        if (tryAcquireShared(arg) < 0)
        	//入隊(duì)阻塞
            doAcquireSharedInterruptibly(arg);
    }
 protected int tryAcquireShared(int acquires) {
            return (getState() == 0) ? 1 : -1;
        }

入隊(duì)阻塞

private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        //入隊(duì),創(chuàng)建節(jié)點(diǎn) 使用共享模式
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            for (;;) {
            //獲取當(dāng)前節(jié)點(diǎn)的前軀節(jié)點(diǎn)
                final Node p = node.predecessor();
                //如果節(jié)點(diǎn)為head節(jié)點(diǎn)
                if (p == head) {
                	//阻塞動(dòng)作比較重,通常會(huì)再嘗試獲取資源,沒有獲取到返回負(fù)數(shù)
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        failed = false;
                        return;
                    }
                }
                //判斷是否可以阻塞
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

countDown方法減一

  public void countDown() {
        sync.releaseShared(1);
    }
  public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }
//嘗試釋放共享鎖
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;
                if (compareAndSetState(c, nextc))
                	//減到0的時(shí)候返回true,進(jìn)行喚醒
                    return nextc == 0;
            }
        }

喚醒邏輯

 private void doReleaseShared() {
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                	//wa為-1時(shí),將其狀態(tài)設(shè)置為0,并且喚醒
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;          
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }
private void unparkSuccessor(Node node) {
        int ws = node.waitStatus;
        if (ws < 0)
        //ws狀態(tài)小于0就將其設(shè)置為0
            compareAndSetWaitStatus(node, ws, 0);
        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;
        }
        if (s != null)
        //s不為空就調(diào)用unpark
            LockSupport.unpark(s.thread);
    }

五. CountDownLatch與Thread.join的區(qū)別

  • CountDownLatch的作用就是允許一個(gè)或多個(gè)線程等待其他線程完成操作,看起來有點(diǎn)類似join() 方法,但其提供了比 join() 更加靈活的API。
  • CountDownLatch可以手動(dòng)控制在n個(gè)線程里調(diào)用n次countDown()方法使計(jì)數(shù)器進(jìn)行減一操作,也可以在一個(gè)線程里調(diào)用n次執(zhí)行減一操作。
  • 而 join() 的實(shí)現(xiàn)原理是不停檢查join線程是否存活,如果 join 線程存活則讓當(dāng)前線程永遠(yuǎn)等待。所以兩者之間相對(duì)來說還是CountDownLatch使用起來較為靈活。

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

相關(guān)文章

  • SpringMVC @RequestMapping注解作用詳解

    SpringMVC @RequestMapping注解作用詳解

    通過@RequestMapping注解可以定義不同的處理器映射規(guī)則,下面這篇文章主要給大家介紹了關(guān)于SpringMVC中@RequestMapping注解用法的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-01-01
  • Java網(wǎng)絡(luò)通信基礎(chǔ)編程(必看篇)

    Java網(wǎng)絡(luò)通信基礎(chǔ)編程(必看篇)

    下面小編就為大家?guī)硪黄狫ava網(wǎng)絡(luò)通信基礎(chǔ)編程(必看篇)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-05-05
  • java反編譯工具jd-gui使用詳解

    java反編譯工具jd-gui使用詳解

    JD-GUI是一個(gè)獨(dú)立的圖形實(shí)用程序,顯示“.class”文件的Java源代碼,本文主要介紹了java反編譯工具jd-gui使用詳解,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-09-09
  • Spring Boot整合Spring Security簡(jiǎn)單實(shí)現(xiàn)登入登出從零搭建教程

    Spring Boot整合Spring Security簡(jiǎn)單實(shí)現(xiàn)登入登出從零搭建教程

    這篇文章主要給大家介紹了關(guān)于Spring Boot整合Spring Security簡(jiǎn)單實(shí)現(xiàn)登入登出從零搭建的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧
    2018-09-09
  • Spring中@DependsOn注解的作用及實(shí)現(xiàn)原理解析

    Spring中@DependsOn注解的作用及實(shí)現(xiàn)原理解析

    這篇文章主要介紹了Spring中@DependsOn注解的作用及實(shí)現(xiàn)原理解析,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-03-03
  • Java關(guān)鍵字詳解之final static this super的用法

    Java關(guān)鍵字詳解之final static this super的用法

    this用來調(diào)用目前類自身的成員變量,super多用來調(diào)用父類的成員,final多用來定義常量用的,static定義靜態(tài)變量方法用的,靜態(tài)變量方法只能被類本身調(diào)用,下文將詳細(xì)介紹,需要的朋友可以參考下
    2021-10-10
  • Java復(fù)制(拷貝)數(shù)組的4種方法:arraycopy()方法、clone() 方法、copyOf()和copyOfRan

    Java復(fù)制(拷貝)數(shù)組的4種方法:arraycopy()方法、clone() 方法、copyOf()和copyOfRa

    這篇文章主要介紹了Java復(fù)制(拷貝)數(shù)組的4種方法:arraycopy()方法、clone() 方法、copyOf()和copyOfRan,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • MyBatis-Plus結(jié)合Layui實(shí)現(xiàn)分頁(yè)方法

    MyBatis-Plus結(jié)合Layui實(shí)現(xiàn)分頁(yè)方法

    MyBatis-Plus 使用簡(jiǎn)單,本文主要介紹使用 service 中的 page 方法結(jié)合 Layui 前端框架實(shí)現(xiàn)分頁(yè)效果,具有一定的參考價(jià)值,感興趣的可以了解一下
    2021-08-08
  • Java微信公眾平臺(tái)開發(fā)(13) 微信JSSDK中Config配置

    Java微信公眾平臺(tái)開發(fā)(13) 微信JSSDK中Config配置

    這篇文章主要為大家詳細(xì)介紹了Java微信公眾平臺(tái)開發(fā)第十三步,微信JSSDK中Config配置,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-04-04
  • Java樹形結(jié)構(gòu)數(shù)據(jù)生成導(dǎo)出excel文件方法記錄

    Java樹形結(jié)構(gòu)數(shù)據(jù)生成導(dǎo)出excel文件方法記錄

    最近好像得罪了poi,遇到的都是導(dǎo)出word、Excel、pdf的問題,下面這篇文章主要給大家介紹了關(guān)于Java樹形結(jié)構(gòu)數(shù)據(jù)生成導(dǎo)出excel文件的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2021-10-10

最新評(píng)論