CountDownLatch倒計數鎖存器
CountDownLatch:用于協同控制一個或多個線程等待在其他線程中執行的一組操作完成,然后再繼續執行
CountDownLatch用法
- 構造方法:CountDownLatch(int count),count指定等待的條件數(任務數、操作數),不可再更改
- 等待方法:await(),阻塞等待線程直到count減少為0,count為0時,不會阻塞,繼續執行
- boolean await(long timeout,TimeUnit
unit):可以設置超時時間的await方法,返回true表示等待條件到達;false表示條件未來到達,但超時了 - long getCount():獲取當前計數值,常用于調試或者測試
ps:CountDownLatch注意事項:只可使用一次,不能重復使用,計數變為0之后,就不可再用
CountDownLatch適用場景
- 等待多個條件完成,countDownLatch(N)這個多個條件可以是:等待N個線程、等待N個操作、等待某操作的N次執行
- 用于并發測試,等待多個線程一起出發
CountDownLatch例子
import java.util.Optional;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.stream.IntStream;public class JDKCountDown {private static final Random random = new Random(System.currentTimeMillis());public static void main(String[] args) throws InterruptedException {Optional.of("準備多線程處理任務。").ifPresent(System.out::println);final CountDownLatch countDownLatch = new CountDownLatch(5);IntStream.rangeClosed(1,5).forEach(i -> new Thread(()->{try {Optional.of("線程" + Thread.currentThread().getName() + " 開始執行任務。").ifPresent(System.out::println);Thread.sleep(random.nextInt(1000));Optional.of("線程" + Thread.currentThread().getName() + " 執行任務結束。").ifPresent(System.out::println);} catch (InterruptedException e) {e.printStackTrace();}countDownLatch.countDown();}, "Thread-"+i).start());countDownLatch.await();Optional.of("多線程處理任務結束。準備第二階段的工作").ifPresent(System.out::println);Optional.of("------------------------------------").ifPresent(System.out::println);Optional.of("FINISH").ifPresent(System.out::println);}
}
執行之后的結果
準備多線程處理任務。
線程Thread-3 開始執行任務。
線程Thread-4 開始執行任務。
線程Thread-5 開始執行任務。
線程Thread-2 開始執行任務。
線程Thread-1 開始執行任務。
線程Thread-2 執行任務結束。
線程Thread-1 執行任務結束。
線程Thread-3 執行任務結束。
線程Thread-5 執行任務結束。
線程Thread-4 執行任務結束。
多線程處理任務結束。準備第二階段的工作
------------------------------------
FINISH
我們手寫一個簡單SimpleCountDown
1.需要一個totalCount指定等待的條件數(任務數、操作數)
2.需要一個countDown計算有幾個線程 結束了
上代碼
/*** 簡單倒計時工具類,用于多線程間協調完成計數操作*/
public class SimpleCountDown {/** 總需要達到的倒計數次數(不可變) */private final int totalCount;/** 當前倒計數值 */private int countDown;/** 是否被取消(volatile保證可見性) */private volatile boolean canceled = false;/*** 構造方法* @param totalCount 需要完成的總倒計數次數*/public SimpleCountDown(int totalCount) {this.totalCount = totalCount;}/*** 執行一次倒計數操作(線程安全)* <p>增加當前計數值并通知所有等待線程</p>*/public void down(){synchronized (this){this.countDown++;this.notifyAll(); // 通知所有等待線程檢查條件}}/*** 等待直到完成所有倒計數操作* @throws InterruptedException 若當前線程被中斷則拋出異常*/public void await() throws InterruptedException {synchronized (this){while (countDown != totalCount){ // 使用while防止虛假喚醒this.wait();}}}
}
測試類
import java.util.Optional;
import java.util.Random;
import java.util.stream.IntStream;/*** 演示SimpleCountDown倒計時門閂的使用示例類*/
public class SimpleCountDownClient {// 隨機數生成器,用于模擬線程任務的隨機執行時間private static final Random random = new Random(System.currentTimeMillis());public static void main(String[] args) throws InterruptedException {// 輸出任務開始提示Optional.of("準備多線程處理任務。").ifPresent(System.out::println);// 初始化計數器為5,表示需要等待5個線程完成final SimpleCountDown countDownLatch = new SimpleCountDown(5);// 啟動5個線程執行任務IntStream.rangeClosed(1, 5).forEach(i -> {new Thread(() -> {try {// 線程任務開始通知Optional.of("線程" + Thread.currentThread().getName() + " 開始執行任務。").ifPresent(System.out::println);// 模擬隨機執行時間(0-1000毫秒)Thread.sleep(random.nextInt(1000));// 任務完成通知Optional.of("線程" + Thread.currentThread().getName() + " 執行任務結束。").ifPresent(System.out::println);} catch (InterruptedException e) {e.printStackTrace();} finally {// 通知計數器完成一個任務countDownLatch.down();}}, "Thread-" + i).start();});// 阻塞等待所有線程完成countDownLatch.await();// 所有線程完成后輸出后續階段提示Optional.of("多線程處理任務結束。準備第二階段的工作").ifPresent(System.out::println);Optional.of("------------------------------------").ifPresent(System.out::println);Optional.of("FINISH").ifPresent(System.out::println);}
}
執行的結果
準備多線程處理任務。
線程Thread-1 開始執行任務。
線程Thread-5 開始執行任務。
線程Thread-3 開始執行任務。
線程Thread-4 開始執行任務。
線程Thread-2 開始執行任務。
線程Thread-5 執行任務結束。
線程Thread-1 執行任務結束。
線程Thread-2 執行任務結束。
線程Thread-3 執行任務結束。
線程Thread-4 執行任務結束。
多線程處理任務結束。準備第二階段的工作
------------------------------------
FINISH