线程同步器CycliBarrier你都不会吗,打击到了我。
上文介绍了CountDownLatch,CountDownLatch虽然可以实现多个线程同步,但是只能使用一次。而本文所要介绍的CycliBarrier,比CountDownLatch强大许多,可以使用多次。下面会从源码角度通透解析CyclicBarrier,觉得不错可以点赞收藏、觉得不好的话,欢迎评论区指正。
两个例子
CyclicBarrier实现一次线程同步,两个线程都执行完之后,才会进入下一步
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main{
//初始值为2,指定同步两个线程
public static CyclicBarrier barrier = new CyclicBarrier(2);
public static void main(String args[]){
ExecutorService pool = Executors.newFixedThreadPool(2);
pool.submit(new Runnable(){
@Override
public void run() {
try{
System.out.println("Thread1 start");
//doSomething
barrier.await();
System.out.println("Thread1 end");
}catch(Exception e) {
e.printStackTrace();
}
}
});
pool.submit(new Runnable(){
@Override
public void run() {
try{
System.out.println("Thread2 start");
//doSometing
barrier.await();
System.out.println("Thread2 end");
}catch(Exception e) {
e.printStackTrace();
}
}
});
pool.shutdown();
}
}
CycliBarrier实现两阶段同步,线程一和线程二完成第一阶段后,才能进入第二阶段。可以看出CycliBarrier于CountDownLatch的区别,可以实现多次同步
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main{
//初始值为2,指定同步两个线程
public static CyclicBarrier barrier = new CyclicBarrier(2);
public static void main(String args[]){
ExecutorService pool = Executors.newFixedThreadPool(2);
pool.submit(new Runnable(){
@Override
public void run() {
try{
System.out.println("Thread1 step1 start");
//doSomething
barrier.await();
System.out.println("Thread1 step1 end");
System.out.println("Thread1 step2 start");
//doSomething
barrier.await();
System.out.println("Thread1 step2 end");
}catch(Exception e) {
e.printStackTrace();
}
}
});
pool.submit(new Runnable(){
@Override
public void run() {
try{
System.out.println("Thread2 step1 start");
//doSomething
barrier.await();
System.out.println("Thread2 step1 end");
System.out.println("Thread2 step2 start");
//doSomething
barrier.await();
System.out.println("Thread2 step2 end");
}catch(Exception e) {
e.printStackTrace();
}
}
});
pool.shutdown();
}
}
// Thread1 step1 start
// Thread2 step1 start
// Thread1 step1 end
// Thread2 step1 end
// Thread2 step2 start
// Thread1 step2 start
// Thread1 step2 end
// Thread2 step2 end
CycliBarrier类图
- parties:同步线程数量
- count:计数器,初始为parties,调用一次await()方法,减一
- barrierCommand: count计数器为0时,会执行barrierCommand
- lock: ReentrantLock类型,用于控制线程同步
- trip: Condition类型,由lock.newConditon()产生
- generation:
源码分析
前言:一次同步过程简称为一代
初始化
注意一点,参数parties,不仅赋值给变量parties,而且赋值给count
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
await方法
调用dowait方法
public int await() throws InterruptedException, BrokenBarrierException {
try {
//没有超时设置超时机制
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
dowait方法
- 调用dowait方法会将count减一
- 如果count减一后,不为零,则进入trip的阻塞队列
- 减一后,为零,唤醒阻塞队列里的所有线程。更新状态,准备下一代。
- 线程唤醒后,检查generation引用指向(进入下一代后,generation会重新指向),指向变化了,说明进入了下一代,当前代结束,所以可以直接返回。
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;
//如果count==0,代表冲破屏障,可以进入下一代(下一次同步过程)
if (index == 0) { // tripped
boolean ranAction = false;
try {
//执行方法
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
//进入下一代
nextGeneration();
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
//减一后不为0,进入trip条件变量的阻塞队列
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 {
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();
}
}
nextGeneration方法
注意上一代结束后,count计数器更新为parties值,这是可以多次使用究极原因
private void nextGeneration() {
// 唤醒上一代阻塞的线程
trip.signalAll();
// 更新状态,准备下一代
count = parties;
generation = new Generation();
}
参考文章
Java并发编程之美