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门闩会使得默认主线程等待所有的工作线程全部结束再执行接下来的。