CountDownLatch和CyclicBarrier都是线程同步的方式,比如多线程有特定的作业顺序的场景.
CountDownLatch
如何使用
doSomethingElse(); // don't let run yet
startSignal.countDown(); // let all threads proceed
doSomethingElse();
doneSignal.await(); // wait for all to finish
和JUC的其他组件类似,CountDownLatch定义了基于AQS框架的内部同步器,并重写了 tryReleaseShared(int releases) 和
tryAcquireShared(int acquires) 两个方法。
AQS的state就是直接通过构造器作为参数传入
CountDownLatch.await()
CountDownLatch.await()就是等待计数器减为0的时候才唤醒. 如何实现的呢?.
通过同步器获取共享方式的同步状态:
CountDownLatch重写了tryAcquireShared(): 若此时state=0才返回正数(表示获取同步状态成功)否则返回负数(失败)
若此时计数器已经减为0, 则立即从await()返回, 否则进入AQS框架定义的acquire()方法:
如果在执行acquire的时候线程被中断了会抛出中断异常返回,否则就是acquire的一般流程, 再次尝试获取一下同步状态, 失败就直接阻塞了(进入队列).
如果万一获取成功了, 会通过 signalNextIfShared() 唤醒node的后继。因为此时节点已经获取同步状态成功了,而且是共享模式,所以node应该唤醒后继节点来获取同步状态,后继节点获取成功后又会唤醒后继…
CountDownLatch.countDown()
直接调用同步器的releaseShared:
如果sync.tryReleaseShared()返回true,则唤醒队列的头节点,如果头节点获取同步状态成功,又会进入上面讲过的 signalNextIfShared(),从而唤醒一个或多个线程。
经典自旋CAS保证 c = c - 1 的原子性。
CyclicBarrier
如何使用
A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point.
The barrier is called cyclic because it can be re-used after the waiting threads are released.
循环屏障,就像所有人等到人齐了才能发车,大概意思是这样。
举个更具体的例子,任务分组,一组多个任务,组级别顺序处理,组内多个任务可以并行处理,那么就可以使用CyclicBarrier。
CyclicBarrier用一个静态私有类对 “屏障”进行抽象。
用一个互斥锁解决线程解决屏障的线程安全问题。等待着的线程就挂在trip这个Condition上面。一旦这个屏障被打破了,应该唤醒所有的线程。
CyclicBarrier提供了两个构造函数,可以通过传入Runnable来指定当最后一个线程到达屏障之后的动作。(由最后到达的线程执行)。
CyclicBarrier.await()
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
final Generation g = generation;
if (g.broken)
throw new BrokenBarrierException();
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
int index = --count;
if (index == 0) { // tripped
Runnable command = barrierCommand;
if (command != null) {
try {
command.run();
} catch (Throwable ex) {
breakBarrier();
throw ex;
}
}
nextGeneration();
return 0;
}
// loop until tripped, broken, interrupted, or timed out
for (;;) {
try {
if (!timed)
trip.await();
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
if (g == generation && ! g.broken) {
breakBarrier();
throw ie;
} else {
// We're about to finish waiting even if we had not
// been interrupted, so this interrupt is deemed to
// "belong" to subsequent execution.
Thread.currentThread().interrupt();
}
}
if (g.broken)
throw new BrokenBarrierException();
if (g != generation)
return index;
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}
-
检查当前屏障是否已经处于broken状态,线程是否被中断;
-
如果当前线程发现现在已经齐了,执行指定的动作,并打破屏障,唤醒所有的线程;
-
否则就是最后那个死循环,只有发生:屏障被打破/某个线程被中断/超时 线程才会出来。某个线程被中断也会导致屏障被打破。