CountDownLatch介紹
CountDownLatch概述
CountDownLatch
一般用作多線程倒計時計數器,強制它們等待其他一組(CountDownLatch
的初始化決定)任務執行完成。- 有一點要說明的是
CountDownLatch
初始化后計數器值遞減到0的時候,不能再復原的,這一點區別於Semaphore
,Semaphore
是可以通過release
操作恢復信號量的。
CountDownLatch使用原理
使用原理
- 創建CountDownLatch並設置計數器值。
- 啟動多線程並且調用CountDownLatch實例的countDown()方法。
- 主線程調用
await()
方法,這樣主線程的操作就會在這個方法上阻塞,直到其他線程完成各自的任務,count值為0,停止阻塞,主線程繼續執行。
使用模板
public class CountDownLatchModule {
//線程數
private static int N = 10;
// 單位:min
private static int countDownLatchTimeout = 5;
public static void main(String[] args) {
//創建CountDownLatch並設置計數值,該count值可以根據線程數的需要設置
CountDownLatch countDownLatch = new CountDownLatch(N);
//創建線程池
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < N; i++) {
cachedThreadPool.execute(() ->{
try {
System.out.println(Thread.currentThread().getName() + " do something!");
} catch (Exception e) {
System.out.println("Exception: do something exception");
} finally {
//該線程執行完畢-1
countDownLatch.countDown();
}
});
}
System.out.println("main thread do something-1");
try {
countDownLatch.await(countDownLatchTimeout, TimeUnit.MINUTES);
} catch (InterruptedException e) {
System.out.println("Exception: await interrupted exception");
} finally {
System.out.println("countDownLatch: " + countDownLatch.toString());
}
System.out.println("main thread do something-2");
//若需要停止線程池可關閉;
// cachedThreadPool.shutdown();
}
運行結果:
main thread do something-1
pool-1-thread-1 do something!
pool-1-thread-2 do something!
pool-1-thread-3 do something!
pool-1-thread-5 do something!
pool-1-thread-6 do something!
pool-1-thread-7 do something!
pool-1-thread-8 do something!
pool-1-thread-4 do something!
pool-1-thread-9 do something!
pool-1-thread-10 do something!
countDownLatch: java.util.concurrent.CountDownLatch@76fb509a[Count = 0]
main thread do something-2
CountDownLatch常用方法
public void await() throws InterruptedException
:調用await()
方法的線程會被掛起,等待直到count
值為0
再繼續執行。public boolean await(long timeout, TimeUnit unit) throws InterruptedException
:同await()
,若等待timeout
時長后,count
值還是沒有變為0,不再等待,繼續執行。時間單位如下常用的毫秒、天、小時、微秒、分鍾、納秒、秒。
public void countDown()
: count值遞減1.public long getCount()
:獲取當前count值。public String toString()
:重寫了toString()方法,多打印了count值,具體參考源碼。
CountDownLatch使用場景
一個程序中有N個任務在執行,我們可以創建值為N的CountDownLatch,當每個任務完成后,調用一下countDown()
方法進行遞減count值
,再在主線程中使用await()
方法等待任務執行完成,主線程繼續執行。
CountDownLatch源碼
構造方法源碼
/**
* Constructs a {@code CountDownLatch} initialized with the given count.
*
* @param count the number of times {@link #countDown} must be invoked
* before threads can pass through {@link #await}
* @throws IllegalArgumentException if {@code count} is negative
*/
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
toString()方法源碼
/**
* Returns a string identifying this latch, as well as its state.
* The state, in brackets, includes the String {@code "Count ="}
* followed by the current count.
*
* @return a string identifying this latch, as well as its state
*/
public String toString() {
return super.toString() + "[Count = " + sync.getCount() + "]";
}
CountDownLatch示例
作為線程啟動信號
代碼
public class CountDownLatchTest {
/**
* a start signal that prevents any worker from proceeding
* until the driver is ready for them to proceed;
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
CountDownLatch startSignal = new CountDownLatch(1);
CountDownLatch doneSignal = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
// create and start threads
new Thread(new Worker(startSignal, doneSignal)).start();
}
// don't let run yet
System.out.println("do something else 1");
// let all threads proceed
startSignal.countDown();
System.out.println("do something else 2");
// wait for all to finish
doneSignal.await();
System.out.println("wait for all to finsh");
}
static class Worker implements Runnable{
private final CountDownLatch startSignal;
private final CountDownLatch doneSignal;
Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
this.startSignal = startSignal;
this.doneSignal = doneSignal;
}
@Override
public void run() {
try {
startSignal.await();
doWork();
doneSignal.countDown();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
void doWork() {
System.out.println("do work!");
}
}
}
運行結果
do something else 1
do something else 2
do work!
do work!
do work!
do work!
do work!
do work!
do work!
do work!
do work!
do work!
wait for all to finsh
從運行結果可以看出:
- 主線程先打印
do something else 1
和do something else 2
。因為startSignal.countDown();
完后,count才為0,子線程才能打印。 - 因為
startSignal.await();
是在子線程內,所有子線程都等待startSignal.countDown()
執行后才能打印do work!
。 doneSignal.await();
等待所有子線程執行后,每次都doneSignal.countDown()
,最后count為0,主線程才執行打印wait for all to finsh
。
作為線程等待完成信號
代碼
public class CountDownLatchTest2 {
/**
* a completion signal that allows the driver to wait
* until all workers have completed.
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
CountDownLatch doneSignal = new CountDownLatch(5);
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
// create and start threads
cachedThreadPool.execute(new Worker(doneSignal, i));
}
// don't let run yet
System.out.println("do something else 1");
// wait for all to finish
doneSignal.await();
System.out.println("===========================count: " + doneSignal.getCount());
System.out.println("do something else 2");
cachedThreadPool.shutdown();
}
static class Worker implements Runnable{
private final CountDownLatch doneSignal;
private final int i;
Worker(CountDownLatch doneSignal, int i) {
this.doneSignal = doneSignal;
this.i = i;
}
@Override
public void run() {
try {
doWork();
doneSignal.countDown();
System.out.println("i = " + i + ", " + doneSignal.toString());
} catch (Exception ex) {
ex.printStackTrace();
}
}
void doWork() {
System.out.println("do work!");
}
}
}
運行結果
do something else 1
do work!
i = 0, java.util.concurrent.CountDownLatch@128abd43[Count = 4]
do work!
i = 1, java.util.concurrent.CountDownLatch@128abd43[Count = 3]
do work!
i = 2, java.util.concurrent.CountDownLatch@128abd43[Count = 2]
do work!
i = 3, java.util.concurrent.CountDownLatch@128abd43[Count = 1]
do work!
i = 4, java.util.concurrent.CountDownLatch@128abd43[Count = 0]
===========================count: 0
do something else 2
do work!
i = 5, java.util.concurrent.CountDownLatch@128abd43[Count = 0]
do work!
i = 6, java.util.concurrent.CountDownLatch@128abd43[Count = 0]
do work!
i = 7, java.util.concurrent.CountDownLatch@128abd43[Count = 0]
do work!
i = 8, java.util.concurrent.CountDownLatch@128abd43[Count = 0]
do work!
i = 9, java.util.concurrent.CountDownLatch@128abd43[Count = 0]
從運行結果可以看出,主線程是等待其他線程運行了5次結束后就打印了do something else 2
信息,因為CountDownLatch數值為5。