CountDownLatch的计数器是一次性的。当计数器值为0时,调用CountDownLatch的await和倒计时方法会立即返回,不会达到线程同步的效果。
CyclicBarrier类可以将计数器重置为。
CyclicBarrier是一个循环屏障,可以让一组线程全部达到一个状态,然后同时执行。
循环意味着它可以在所有等待的线程完成执行并重置CyclicBarrier的状态后重用。
也就是说,在调用await方法之后,屏障线程将被阻塞。这个阻塞点被称为障碍点。在所有线程都调用了await方法之后,线程将突破障碍并继续向下运行。
CyclicBarrier例子
运行结果:
公共循环壁垒(内部各方,可运行壁垒行动){ 0
if (parties=0)抛出新的IllegalArgumentException();
this.parties=parties
this.count=parties
this . BarrierCommand=BarrierAction;
}
第一个参数是计数器的初始值,第二个参数Runable是计数器值为0时执行的任务。
多个线程正在相互等待。如果计数器值为N,那么稍后调用await方法的N-1个线程将被阻塞,因为它们到达了障碍点。当第N个线程调用等待时,计数器值为0,然后第N个线程会发送通知唤醒前面的N-1个线程。
当所有线程到达障碍点时,它们可以继续一起向下执行。
00-1010也许各种教程中对countdowlatch的描述让我们认为countdowlatch和CyclicBarries不一样,但本质上是一样的,只是计数器为0时开始运行。
导入Java . util . concurrent . countdowlatch;
导入Java . util . concurrent . executorservice;
导入Java . util . concurrent . executors;
公共类CountDownLatchTest {
私有静态countdowlatch countdowlatch=new countdowlatch(2);
公共静态void main(String[]args){ 0
executor service executor service=executors . newfixedthreadpool(2);
executorService.submit(新的Runnable(){ 0
public void run(){ 0
尝试{
system . out . println(thread . currentthread()’任务1-1 ‘);
system . out . println(thread . currentthread()“进入屏障”);
countDownLatch.countDown .倒计时();
countdowlatch . await();
System.out.println(线程. cu
rrentThread() + ” enter out barrier”);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
executorService.submit(new Runnable() {
public void run() {
try {
System.out.println(Thread.currentThread() + ” task 1-2″);
System.out.println(Thread.currentThread() + ” enter in barrier”);
countDownLatch.countDown();
countDownLatch.await();
System.out.println(Thread.currentThread() + ” enter out barrier”);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
executorService.shutdown();
}
}
使用CountDownLatch实现所有的线程等待所有线程执行完毕之后才继续往下执行确实有点别扭,但这是api层面的别扭,跟CountDonwLatch的本质没有关系。
CyclicBarrier的可复用性
运行结果:
实现原理探究
CyclicBarrier基于独占锁实现,底层还是基于AQS的。
parties用来记录线程个数,表示多少个线程调用await后,所有线程才会冲破屏障继续往下运行。
count一开始等于parties,每当有线程调用await方法就递减1,当count为0时就表示所有线程都到了屏障点。
CycleBarier是可以被复用的,parties始终用来记录总的线程个数,当count计数器值变为0后,会将parties的值赋给count,从而进行复用。
barrierCommand表示任务,这个任务的执行时机是当所有线程都到达屏障点后。
使用lock首先保证了更新计数器count的原子性。使用lock的条件变量trip支持线程间使用await和signal操作进行同步。
变量generation内部变量broken用来记录当前屏障是否被打破。broken并没有被声明为volatile的,因为是在锁内使用变量,所以不需要声明。
int await() 方法
当前线程调用该方法会被阻塞,直到满足下面条件之一才会返回:
1.parties个线程都调用了await()方法,也就是线程都到了屏障点;
2.其他线程调用了当前线程的interrupt()方法中断了当前线程,则当前线程会抛出InterruptedException异常而返回;
3.与当前屏障点关联的Generation对象的broken标志被设置为true时,会抛出BrokenBarrierException异常,然后返回。
boolean await(long timeout, TimeUnit unit) 方法
当前线程调用该方法时会被阻塞,直到满足下面条件之一才会返回:
1.parties个线程都调用了await()方法,也就是线程都到了屏障点,这时候返回true;
2.设置的超时时间到了后返回false;
3.其他线程调用当前线程的interrupt()方法中断了当前线程,则当前线程会抛出InterruptedException异常然后返回;
4.与当前屏障点关联的Generation对象的broken标志被设置为true时,会抛出BrokenBarrierException异常,然后返回。
int dowait(boolean timed, long nanos) 方法
该方法实现了CyclicBarrier的核心功能。