1. 倒計時門閂同步器
倒計時門閂會導致一條或多條線程在“門口”一直等待,直到另一條線程打開這扇門,線程才得以繼續運行。它是由一個計數變量和兩個操作組成的,這兩個操作分別是“導致一條線程等待直到計數變爲0”以及“遞減計數變量”。
類java.util.concurrent.CountDownLatch實現了倒計時門閂同步器。
應用場景:比如流水線,後面的工序必須等到前面的工序做完之後才能繼續做。
2. 常用方法
(1)CountDownLatch(int count)
倒計時門閂同步器的構造方法,通過參數count指定計數個數,來初始化一個CountDownLatch的實例。當count的值是負數,該方法會拋出java.lang.IllegalArgumentException。
(2)void await()
除非線程被中斷,否則強制調用線程一直等到計數倒數到0。線程被中斷時會拋出java.lang.InterruptedException。當count是0時,該方法立即返回。
(3)boolean await(long timeout, TimeUnit unit)
除非線程被中斷,否則強制調用線程一直等到計數倒數至0或者以Unit作爲時間單元的timeout超時。線程被中斷時會拋出java.lang.InterruptedException。當count是0時,該方法立即返回ture;當超時等待時間時,返回fasle.
(4)void countDown()
遞減計數,當計數降至0時,釋放所有等待線程。當該方法被調用時count已經爲0,那麼什麼也不會發生。
(5)long getCount()
返回當前的計數,該方法對於測試和調試很有用。
(6)String toString()
返回一條標識這個門閂及其狀態的字符串。狀態是用中括號括住的字符串常量“Count = ”拼接上當前的計數。
3.
(1)倒計時門閂示例1
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//倒計時門閂同步器簡單實例
public class CountDownLatchDemo1 {
public static void main(String[] args)
{
CountDownLatch countDownLatch = new CountDownLatch(3);
Runnable r = new Runnable(){
@Override
public void run()
{
System.out.println(Thread.currentThread().getName()+": latch count is " + countDownLatch.getCount());
countDownLatch.countDown(); //遞減計數
System.out.println(Thread.currentThread().getName()+": after countDown, latch count is " + countDownLatch.getCount());
}
};
ExecutorService executorService = Executors.newFixedThreadPool(3);
System.out.print("before latch await\n");
for(int i = 0; i < 3; i++)
{
executorService.execute(r);
}
try {
countDownLatch.await(); //只有當計數倒數至0時,後面的主線程纔會繼續執行。
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("after latch await");
executorService.shutdownNow();
}
}
運行結果:
before latch await
pool-1-thread-2: latch count is 3
pool-1-thread-2: after countDown, latch count is 2
pool-1-thread-1: latch count is 3
pool-1-thread-1: after countDown, latch count is 1
pool-1-thread-3: latch count is 3
pool-1-thread-3: after countDown, latch count is 0
after latch await
默認主線程首先創建了一個倒計時門閂,這個門閂會使得默認主線程等待所有的工作線程全部結束再執行。
默認主線程接下來創建了一個帶有run()方法的runnable,它會被後續創建的工作線程執行。在這個run()方法中,先打印在countDown遞減計數之前的計數值,然後開始遞減計數,最後再打印一次countDown遞減計數之後的計數值。
創建好runnable之後,然後創建了一個固定線程數爲3的線程池,先打印了一條消息“before latch await”,再依次把runnable傳入線程池中。這一動作會啓動線程運行run()方法。
接着調用await()方法,主線程只能等到count遞減至0時,才能繼續往下執行,也就是輸出“after latch await ”。
(2)倒計時門閂示例2
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CountDownLatchDemo2 {
final static int NThreads = 3;
public static void main(String[] args)
{
final CountDownLatch startSignal = new CountDownLatch(1);
final CountDownLatch doneSignal = new CountDownLatch(3);
Runnable r = new Runnable() {
@Override
public void run()
{
try {
report("entered run()");
startSignal.await(); //等待主線程就緒
report("doing work");
Thread.sleep((int)(Math.random()*1000)); //模擬工作過程
doneSignal.countDown();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
void report(String s)
{
System.out.println(System.currentTimeMillis()+":"+Thread.currentThread()+ ":" + s);
}
};
ExecutorService executorService = Executors.newFixedThreadPool(NThreads);
for(int i = 0; i < NThreads; i++)
{
executorService.execute(r);
}
try {
System.out.println("main thread doing something");
Thread.sleep(1000); //模擬執行其他工作,得以讓所有工作線程依次執行run()方法,進而調用startSignal.await()
startSignal.countDown();
System.out.println("main thread doing something else");
doneSignal.await(); //等待所有線程結束,即計數遞減到0,才能關閉executorService
executorService.shutdownNow();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
運行結果:
main thread doing something
1543026469731:Thread[pool-1-thread-2,5,main]:entered run()
1543026469731:Thread[pool-1-thread-3,5,main]:entered run()
1543026469731:Thread[pool-1-thread-1,5,main]:entered run()
main thread doing something else
1543026470733:Thread[pool-1-thread-3,5,main]:doing work
1543026470733:Thread[pool-1-thread-1,5,main]:doing work
1543026470733:Thread[pool-1-thread-2,5,main]:doing work
默認主線程首先創建了一對倒計時門閂。這個startSignal門閂會在默認主線程就緒之前禁止任何工作線程線程執行,而doneSignal門閂會使得默認主線程等待所有的工作線程全部結束再執行接下來的。