一、CountDownLatch能做什么
CountDownLatch是java.util.concurrent包中的一個類,它主要用來協(xié)調(diào)多個線程之間的同步,起到一個同步器的作用??偟膩碚f,CountDownLatch讓一個或多個線程在運(yùn)行過程中的某個時間點(diǎn)能停下來等待其他的一些線程完成某些任務(wù)后再繼續(xù)運(yùn)行。
類似的任務(wù)可以使用線程的 join() 方法實(shí)現(xiàn):在等待時間點(diǎn)調(diào)用其他線程的 join() 方法,當(dāng)前線程就會等待join線程執(zhí)行完之后才繼續(xù)執(zhí)行,但 CountDownLatch 實(shí)現(xiàn)更加簡單,并且比 join 的功能更多。
二、CountDownLatch怎么用
CountDownLatch的構(gòu)造函數(shù)接收一個 int 型參數(shù)作為計(jì)數(shù)器,例如想讓N任務(wù)完成之后才能繼續(xù)執(zhí)行,創(chuàng)建CountDownLatch時傳入?yún)?shù)N;
需要等待的線程會調(diào)用CountDownLatch的await方法,該線程就會阻塞直到計(jì)數(shù)器的值減為0,而達(dá)到自己預(yù)期的線程會調(diào)用CountDownLatch的countDown()方法,計(jì)數(shù)器N的值減1。由于countDown方法可以在任何地方調(diào)用,所以計(jì)數(shù)器N既可以代表N個線程,也可以是一個線程的N個執(zhí)行步驟。代表多個線程時,只需要將這個CountDownLatch的引用傳到線程里面即可。
N的值為1時,退化為單一事件,即由一個線程來通知其他線程,效果等同于對象的wait和notifyAll;N為0時,調(diào)用await方法不會阻塞當(dāng)前線程。
上圖中,等待線程為4和5,計(jì)數(shù)器的值初始化為3,當(dāng)線程1,2,3都完成調(diào)用countDown()方法后,線程4,5才會從await()方法返回,繼續(xù)執(zhí)行后面的代碼。
綜上,使用CountDownLatch有三個主要步驟
首先創(chuàng)建CountDownLatch實(shí)例時通過構(gòu)造函數(shù)指定計(jì)數(shù)器的值N;
然后需要等待的線程調(diào)用CountDownLatch實(shí)例的await方法,等待計(jì)數(shù)器的值將為0;
被等待的線程在任務(wù)完成時調(diào)用CountDownLatch實(shí)例的countDown方法,計(jì)數(shù)器的值減一。
一個例子:有三個線程解析表格中的數(shù)據(jù),主線程需要等到表格解析完成后才執(zhí)行后面的操作
public class CountDownLatchTest {
public static void main(String[] args) throws Exception{
/*創(chuàng)建CountDownLatch實(shí)例,計(jì)數(shù)器的值初始化為3*/
final CountDownLatch downLatch = new CountDownLatch(3);
/*創(chuàng)建三個線程,每個線程等待1s,表示執(zhí)行比較耗時的任務(wù)*/
for(int i = 0;i < 3;i++){
final int num = i;
new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(String.format("thread %d has finished",num));
/*任務(wù)完成后調(diào)用CountDownLatch的countDown()方法*/
downLatch.countDown();
}
}).start();
}
/*主線程調(diào)用await()方法,等到其他三個線程執(zhí)行完后才繼續(xù)執(zhí)行*/
downLatch.await();
System.out.print("all threads have finished,main thread will continue run");
}
}
運(yùn)行結(jié)果:
三、CountDown 源碼分析
分析CountDownLatch的源碼我們可以知道,它是使用了一個內(nèi)部同步器AQS來實(shí)現(xiàn)屏蔽功能的。只有當(dāng)count的值為零時,同步器的tryAcquireShared的結(jié)果為1,其他時候都是-1。詳細(xì)代碼如下:
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
Sync(int count) {
setState(count);
}
int getCount() {
return getState();
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
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))
return nextc == 0;
}
}
}
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
四、CountDownLatch 的不足
CountDownLatch是一次性的,不可能重新初始化或者修改其內(nèi)部計(jì)數(shù)器的值,當(dāng)CountDownLatch使用完畢后,它不能再次被使用。