Semaphore,CountdownLatch,CyclicBarrier都是java提供的同步辅助类。
上一篇对Semaphore有了一定的了解,本篇则针对CountdownLatch以及CyclicBarrier进行一定的总结。
一、CountdownLatch
1)CountdownLatch是什么?
CyclicBarrier的字面意思是可循环使用(Cyclic)的屏障(Barrier)
能够使一个线程等待其他线程完成各自的工作之后再执行。例如zookeeper分布式锁的实现。而Semaphore则主要强调的是资源有限。
2)CountdownLatch使用场景
比如老公陪同媳妇去医院,媳妇等候医生看病,老公则针对单子去拿药,两者都完工,则一起回家。再比如老板等10个员工开会,只有10个员工都到齐了,会议才能开始等等场景。
3)应用demo
举例说明老板等待员工开会的场景:
public static void main(String[] args) {
final CountDownLatch latch = new CountDownLatch(15);
for (int i = 0; i < 15; i++) {
Random random = new Random();
final int timer = random.nextInt(1000);
new Thread(() -> {
try {
System.out.println("子线程" + Thread.currentThread().getName() + "正在赶路");
Thread.sleep(1000 + timer);
latch.countDown();
System.out.println("子线程" + Thread.currentThread().getName() + "到会议室了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
try {
System.out.println("领导等待员工会议室开会...");
latch.await();
System.out.println("员工都来了,会议开始");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
执行结果:
子线程Thread-1正在赶路
子线程Thread-3正在赶路
子线程Thread-0正在赶路
子线程Thread-2正在赶路
子线程Thread-4正在赶路
子线程Thread-5正在赶路
子线程Thread-6正在赶路
子线程Thread-7正在赶路
子线程Thread-8正在赶路
子线程Thread-9正在赶路
子线程Thread-10正在赶路
子线程Thread-11正在赶路
子线程Thread-12正在赶路
子线程Thread-13正在赶路
领导等待员工会议室开会…
子线程Thread-14正在赶路
子线程Thread-5到会议室了
子线程Thread-1到会议室了
子线程Thread-12到会议室了
子线程Thread-13到会议室了
子线程Thread-4到会议室了
子线程Thread-6到会议室了
子线程Thread-2到会议室了
子线程Thread-0到会议室了
子线程Thread-3到会议室了
子线程Thread-10到会议室了
子线程Thread-14到会议室了
子线程Thread-8到会议室了
子线程Thread-9到会议室了
子线程Thread-7到会议室了
子线程Thread-11到会议室了
员工都来了,会议开始
4)源码分析
核心关注方法只有两个,latch.countDown();和latch.await();
countDown() 方法每次调用都会将 state 减 1,直到state 的值为 0;而 await 是一个阻塞方法,当 state 减 为 0 的时候,await 方法才会返回。await 可以被多个线程调用,大家在这个时候脑子里要有个图:所有调用了await 方法的线程阻塞在 AQS 的阻塞队列中,等待条件满(state == 0),将线程从队列中一个个唤醒过来。
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
在 CountDownLatch 内部写了一个 Sync 并且继承了 AQS 这个抽象类重写了 AQS中的共享锁方法。这段代码主要是判定当前线程是否获取到了共享锁

public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null;
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
二、CyclicBarrier
1)CyclicBarrier是什么?
栅栏屏障,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行。
简单理解就是:多个线程之间互相等待,满足条件在同一时间进行。
2)CyclicBarrier使用场景
可以用于多线程计算数据,最后合并计算结果的场景。
又比如旅行团旅行,需要大家都到齐,证件都办理好,才能出发。
3)应用demo
旅行团旅行,需要大家都到齐,证件都办理好,上旅行车才能出发。
public class CyclicBarrierTest1 extends Thread{
private final CyclicBarrier barrier;
private final Random random = new Random();
public CyclicBarrierTest1(String name,CyclicBarrier barrier) {
super(name);
this.barrier = barrier;
}
@Override
public void run() {
try {
Thread.sleep(random.nextInt(2000));
System.out.println(Thread.currentThread().getName() + " - 已经到达旅行团");
barrier.await();
Thread.sleep(random.nextInt(2000));
System.out.println(Thread.currentThread().getName() + " - 证件已经办理好");
barrier.await();
Thread.sleep(random.nextInt(2000));
System.out.println(Thread.currentThread().getName() + " - 已经上旅行车");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
super.run();
}
public static void main(String[] args) {
CyclicBarrier cyclicBarrier=new CyclicBarrier(5);
for (int i=0;i<5;i++){
new CyclicBarrierTest1("游客-" + (i + 1), cyclicBarrier).start();
}
}
}
游客-5 - 已经到达旅行团
游客-3 - 已经到达旅行团
游客-2 - 已经到达旅行团
游客-1 - 已经到达旅行团
游客-4 - 已经到达旅行团
游客-2 - 证件已经办理好
游客-4 - 证件已经办理好
游客-1 - 证件已经办理好
游客-3 - 证件已经办理好
游客-5 - 证件已经办理好
游客-1 - 已经上旅行车
游客-2 - 已经上旅行车
游客-3 - 已经上旅行车
游客-5 - 已经上旅行车
游客-4 - 已经上旅行车