高併發(8)- 線程併發工具類-CountDownLatch
前言
上篇文章講解了線程的併發工具類之ForkJoin,本文就來講解CountDownLatch併發工具類
ForkJoin的核心思想是分而治之
CountDownLatch的則是一組線程等待其他的線程完成工作以後在執行。
什麼是CountDownLatch
CountDownLatch是一組線程等待其他線程工作完成以後在執行。例如:一個框架在啓動的時候,需要加載各種功能,只有當這些功能加載完成之後,纔可以運行主線程,這個時候就可以用到CountDownLatch了,CountDownLatch通過計數器來實現,計數器的初始值是線程的數量。每當一個線程執行完畢後,計數器的值就-1,當計數器的值爲0時,表示所有線程都執行完畢,然後在閉鎖上等待的線程就可以恢復工作了
如圖所示,我們定義了一個計數器cnt = 5,TW1和TW2兩個線程執行了await()方法,就需要等待計數器減少爲0.
- 所以Td執行了一個操作,CountDown了一次,cnt-1=4
- Tb線程也操作了一次,CountDown了一次,cnt-1=3
- Td線程又操作了一次,CountDown了一次,cnt-1=2
- Ta線程也操作了一次,CountDown了一次,cnt-1=1
- Tc線程也操作了一次,CountDown了一次,cnt-1=0
因爲這時候cnt計數器=0所以,TW1和TW2兩個線程也便推出了等待,繼續運行
注意
- 一個CountDownLatch可以被多個線程等待,而不是隻能被一個線程使用,如圖所示TW1和TW2用的同一個CountDownLatch。
- 一個線程也可以多次CountDown操作,如圖所示,Td線程進行了兩次CountDown操作。
- 一個線程在CountDown之後還是可以繼續運行的,不會在CountDown之後停止,如圖所示,Ta和Td兩個線程在CountDown之後繼續運行。
CountDownLatch實現
- countDownLatch類中只提供了一個構造器:
//參數count爲計數值
public CountDownLatch(int count) {};
- countDownLatch類中還有三個方法比較重要
//調用await()方法的線程會被掛起,它會等待直到count值爲0才繼續執行
public void await() throws InterruptedException { };
//和await()類似,只不過等待一定的時間後count值還沒變爲0的話就會繼續執行
public boolean await(long timeout, TimeUnit unit) throws InterruptedException { };
//將count值減1
public void countDown() { };
- 具體代碼實現
/**
* @version 1.0
* @Description CountDownLatchDemo
* @Author wb.yang
*/
public class CountDownLatchDemo {
/**
* 創建一個計數器爲6的CountDownLatch
*/
static CountDownLatch latch = new CountDownLatch(6);
/**
* 初始化線程(只有一步,有4個)
*/
private static class InitThread implements Runnable {
@Override
public void run() {
System.out.println("Thread_" + Thread.currentThread().getId()
+ " 準備初始化工作......");
//初始化線程完成工作了,countDown方法只扣減一次;
latch.countDown();
System.out.println("Thread_" + Thread.currentThread().getId()
+ " .繼續做剩餘的工作");
}
}
/**
* 業務線程
*/
private static class BusinessThread implements Runnable {
@Override
public void run() {
try {
// 等待
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("BusiThread_" + Thread.currentThread().getId()
+ "運行中-----");
}
}
public static void main(String[] args) throws InterruptedException {
//單獨的初始化線程,初始化分爲2步,需要扣減兩次
new Thread(new Runnable() {
@Override
public void run() {
SleepTools.ms(1);
System.out.println("Thread_" + Thread.currentThread().getId()
+ "準備初始化工作第一步......");
latch.countDown();//每完成一步初始化工作,扣減一次
System.out.println("開始第二步工作.......");
SleepTools.ms(1);
System.out.println("Thread_" + Thread.currentThread().getId()
+ "準備初始化工作第二步......");
latch.countDown();//每完成一步初始化工作,扣減一次
}
}).start();
//開始主要業務線程
new Thread(new BusinessThread()).start();
for (int i = 0; i <= 3; i++) {
//執行三次初始化工作
Thread thread = new Thread(new InitThread());
thread.start();
}
latch.await();
System.out.println("main方法工作........");
}
}
代碼中可以看到,我們定義了一個CountDownLatch,計數器爲6,然後以一個業務線程,需要等待CountDownLatch執行完之後才能執行。還有六個初始化線程。
- 運行一個初始化線程
- 運行業務線程
- 進行剩下的四初始化線程
- 主線程等待await,等待CountDownLatch
再看下運行結果
我們可以看到,先是初始化線程運行,進行了兩次CountDown,證明一個線程可以多次CountDown,然後是剩下四個初始化線程進行CountDown操作,並且在CountDown操作後技術執行代碼,然後是主業務線程在CountDown爲0的時候執行,由於主線程也是await,所以也需要CountDown爲0纔可以執行,也證明了一個CountDownLatch可以被多個線程使用。