CountDownLatch是一个线程并发流程工具,它允许一个或者多个线程阻塞直到一些线程执行完毕,才会继续执行,CountDownLatch会存在一个计数器,当 await方法调用,那么后面的线程都会被阻塞,直到由于countDown()方法的调用而导致当前计数达到零,后续被阻塞的线程才会继续执行,这个计数器是一次性的,不能重置,如果需要重置计数,请考虑使用CyclicBarrier
CountDownLatch内部是通过AQS实现并发流程控制,在的内部有一个AQS类
/**
* 用于控制 CountDownLatch.
* 用AQS的状态代替计数器.
*/
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
//传入计数值,将其设置为AQS状态, setState(count)是父类的一个方法,状态变化整体是可见的
Sync(int count) {
setState(count);
}
//获得当前的状态。同样调的是父类的方法
int getCount() {
return getState();
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
protected boolean tryReleaseShared(int releases) {
// 自旋的方式,判断状态如果为0直接执行被阻塞的线程
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
//利用CAS更新状态
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
//父类方法用于设置状态
/**
* 设置AQS状态
*
*/
protected final void setState(int newState) {
// private volatile int state;,保证可见行
state = newState;
}
//用于阻塞后续所有的线程
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
//阻塞后续线程,直到计数器为0或者超时
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
/**
*
* 计数器减一,当计数器为0的时候后续阻塞线程会继续执行
*
* 如果计数器已经为0,调用此方法不会做任何事
*/
public void countDown() {
//利用AQS的状态量去控制
sync.releaseShared(1);
}
/**
* 返回计数器
*/
public long getCount() {
return sync.getCount();
}
构造方法
/**
* 初始化一个对象,并设置计数器数量和AQS的状态量
* 如果传入一个负数回报参数异常
*/
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
案例
/**
* 描述: 模拟一个连队,所有人都到齐了才统一出发
*/
public class CountDownLatchDemo1 {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(12);
ExecutorService service = Executors.newFixedThreadPool(12);
for (int i = 0; i < 12; i++) {
final int no = i + 1;
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
Thread.sleep((long) (Math.random() * 10000));
System.out.println("No." + no + "达到连队。");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
latch.countDown();
}
}
};
service.submit(runnable);
}
System.out.println("所有人都到齐了.....");
latch.await();
System.out.println("所有人都到齐了,出发!!!!!。");
}
}
/**
* 描述: 模拟100米跑步,5名选手都准备好了,只等裁判员一声令下,所有人同时开始跑步。当所有人都到终点后,比赛结束。
*/
public class CountDownLatchDemo1And2 {
public static void main(String[] args) throws InterruptedException {
CountDownLatch begin = new CountDownLatch(1);
CountDownLatch end = new CountDownLatch(5);
ExecutorService service = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
final int no = i + 1;
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("No." + no + "准备完毕,等待发令枪");
try {
begin.await();
System.out.println("No." + no + "开始跑步了");
Thread.sleep((long) (Math.random() * 10000));
System.out.println("No." + no + "跑到终点了");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
end.countDown();
}
}
};
service.submit(runnable);
}
//裁判员检查发令枪...
Thread.sleep(5000);
System.out.println("发令枪响,比赛开始!");
begin.countDown();
end.await();
System.out.println("所有人到达终点,比赛结束");
}
}