参考:https://www.pdai.tech/md/java/thread/java-thread-x-juc-tool-countdownlatch.html
1 CountDownLatch
CountDownLatch没有实现其他接口,其仍然依赖AQS实现。在创建CountDownLatch时,需要制定当前已经被申请的锁资源数量。
CountDownLatch的构造方法:
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
CountDownLatch有两个核心方法需要关注:
await:等待CountDownLatch的状态为0
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);//调用AQS中的申请共享资源方法
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)//调用子类的具体实现,尝试申请资源
doAcquireSharedInterruptibly(arg);//调用AQS中方法,以自旋的形式申请资源
}
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; // help GC
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
countDown:CountDownLatch的状态-1
public void countDown() {
sync.releaseShared(1);
}
调用AQS的释放共享锁资源
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
可以看出两个方法都依赖于内部类实现:
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
Sync(int count) {
setState(count);
}
int getCount() {
return getState();
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;//当资源为0时申请资源成功
}
protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
例子:
一个线程T1向list中增加元素,一个线程T2在list个数为5时发出警告:
著作权归https://pdai.tech所有。
链接:https://www.pdai.tech/md/java/thread/java-thread-x-juc-tool-countdownlatch.html
public class T3 {
volatile List list = new ArrayList();
public void add(int i){
list.add(i);
}
public int getSize(){
return list.size();
}
public static void main(String[] args) {
T3 t = new T3();
CountDownLatch countDownLatch = new CountDownLatch(1);
new Thread(() -> {
System.out.println("t2 start");
if(t.getSize() != 5){
try {
countDownLatch.await();
System.out.println("t2 end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"t2").start();
new Thread(()->{
System.out.println("t1 start");
for (int i = 0;i<9;i++){
t.add(i);
System.out.println("add"+ i);
if(t.getSize() == 5){
System.out.println("countdown is open");
countDownLatch.countDown();
}
}
System.out.println("t1 end");
},"t1").start();
}
}
可以这样理解:创建CountDownLatch时,已经有一个资源被分配出去了。所以当T2遇到list数量不是5的时候就阻塞,等到T1加入元素到list数量等于5的时候,唤醒了T2进行告警。
2 CyclicBarrier
CyclicBarrier依赖ReentrantLock实现
核心方法:
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;//当前正在等待的线程数量-1
if (index == 0) { // 已经没有线程在等待了
boolean ranAction = false;
try {
//执行任务
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;//执行完成
nextGeneration();
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
// 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();
}
}
private void nextGeneration() {
// signal completion of last generation
trip.signalAll();//唤醒所有正在等待的线程
// set up next generation
count = parties;//进入下一个阶段所以cout返回初始值
generation = new Generation();//新屏障
}
private void breakBarrier() {
//打破屏障初始化锁状态
generation.broken = true;
count = parties;
trip.signalAll();
}
例子:
线程t1,t2和主线程执行完CyclicBarrier 对象的await方法后,barrierAction才开始运行。
著作权归https://pdai.tech所有。
链接:https://www.pdai.tech/md/java/thread/java-thread-x-juc-tool-cyclicbarrier.html
class MyThread extends Thread {
private CyclicBarrier cb;
public MyThread(String name, CyclicBarrier cb) {
super(name);
this.cb = cb;
}
public void run() {
System.out.println(Thread.currentThread().getName() + " going to await");
try {
cb.await();
System.out.println(Thread.currentThread().getName() + " continue");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class CyclicBarrierDemo {
public static void main(String[] args) throws InterruptedException, BrokenBarrierException {
CyclicBarrier cb = new CyclicBarrier(3, new Thread("barrierAction") {
public void run() {
System.out.println(Thread.currentThread().getName() + " barrier action");
}
});
MyThread t1 = new MyThread("t1", cb);
MyThread t2 = new MyThread("t2", cb);
t1.start();
t2.start();
System.out.println(Thread.currentThread().getName() + " going to await");
cb.await();
System.out.println(Thread.currentThread().getName() + " continue");
}
}
3 CountDownLatch和CyclicBarrier的区别
CountDownLatch是一次性的,CyclicBarrier是可复用的