线程 | 并发工具的使用-CyclicBarrier

线程-线程工具-CyclicBarrier



前言

前面介绍了CountDownLatch,那CyclicBarrier和它有什么区别呢?


提示:以下是本篇文章正文内容,下面案例可供参考

一、CyclicBarrier是什么?

CyclicBarrier 也是一种多线程并发控制的实用工具,和 CountDownLatch 一样具有等待计数的功能,但是相比于 CountDownLatch 功能更加强大,因为CountDownLatch是一次性的,而CyclicBarrier是可以循环的。
主要作用使用它进行线程的同步,进行计数循环、重置。
可以理解为,去火锅店吃火锅,吃完一桌走人,再重新来一桌人。

二、主要参数与方法

1.主要参数

参数含义
parties调用的线程数
barrierAction触发障碍时候执行任务

2.方法

//将线程处于休眠状态,所有的线程到达临界点
public int await() throws InterruptedException, BrokenBarrierException

//更新状态,唤醒all
nextGeneration()

//是否处于断开状态
boolean isBroken()

//获取临界点上的线程数
int getNumberWaiting()

//重置为初始状态
reset()

3.构造方法

public CyclicBarrier(int parties) {
    this(parties, null);
}

public CyclicBarrier(int parties, Runnable barrierAction) {
    if (parties <= 0) throw new IllegalArgumentException();
    this.parties = parties;
    this.count = parties;
    this.barrierCommand = barrierAction;
}

三、核心方法源码

dowait

private int dowait(boolean timed, long nanos)
    throws InterruptedException, BrokenBarrierException,
           TimeoutException {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        //生成一个实例
        final Generation g = generation;
        //broken判断是否为断开,初始化为false
        if (g.broken)
            throw new BrokenBarrierException();
        //判断线程是否终止
        if (Thread.interrupted()) {
            //如果终止,broken设置为true,唤醒所有
            breakBarrier();
            throw new InterruptedException();
        }
        //自减一
        int index = --count;
        //都到达临界点,出发临界点任务
        if (index == 0) {  // tripped
            boolean ranAction = false;
            try {
                final Runnable command = barrierCommand;
                if (command != null)
                    command.run();
                ranAction = true;
                nextGeneration();
                return 0;
            } finally {
                //任务执行完毕,唤醒所有,broken设置为断开
                if (!ranAction)
                    breakBarrier();
            }
        }

        // loop until tripped, broken, interrupted, or timed out
        for (;;) {
            try {
                //timed默认为false,挂起当前任务
                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();
    }
}

四、实践

同样还是利用四个人吃火锅在作为例子。通过调用await相互等待,到了临界点的时候出发临界点的线程任务。

private static CyclicBarrier cyclicBarrier = new CyclicBarrier(4,() ->{
    System.out.println("人到齐了,开吃!");
});


public static void main(String[] args) {
    ExecutorService executorService = Executors.newFixedThreadPool(4);
    IntStream.range(0,4).forEach(i ->{
        executorService.submit(() ->{
            System.out.println(Thread.currentThread().getName() + "来吃火锅");
            try {
                cyclicBarrier.await();
                System.out.println(Thread.currentThread().getName() + "动筷子");
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }

        });
    });
    executorService.shutdown();
}

输出结果
在这里插入图片描述

1.分享一个网上的使用例子

// 订单队列
Vector<P> pos;
// 派送单队列
Vector<D> dos;
// 执行回调的线程池 
Executor executor = 
  Executors.newFixedThreadPool(1);
final CyclicBarrier barrier =
  new CyclicBarrier(2, ()->{
    executor.execute(()->check());
  });
  
void check(){
  P p = pos.remove(0);
  D d = dos.remove(0);
  // 执行对账操作
  diff = check(p, d);
  // 差异写入差异库
  save(diff);
}
  
void checkAll(){
  // 循环查询订单库
  Thread T1 = new Thread(()->{
    while(存在未对账订单){
      // 查询订单库
      pos.add(getPOrders());
      // 等待
      barrier.await();
    }
  });
  T1.start();  
  // 循环查询运单库
  Thread T2 = new Thread(()->{
    while(存在未对账订单){
      // 查询运单库
      dos.add(getDOrders());
      // 等待
      barrier.await();
    }
  });
  T2.start();
}

总结

对比一下CountDownLacth与CyclicBarrier

  1. CountDownLatch 强调一个线程等多个线程完成某件事情。CyclicBarrier 是多个线程互等,一起完成。
  2. CountDownLacth是一次性的,而CyclicBarrier 的计数器是可以循环利用的,而且具备自动重置的功能,一旦计数器减到 0 会自动重置到你设置的初始值。
  3. CyclicBarrier 可以设置回调函数
  4. 调用 CountDownLatch 的 countDown 方法后,当前线程并不会阻塞,会继续往下执行;而调用 CyclicBarrier 的 await 方法,会阻塞当前线程,直到 CyclicBarrier 指定的线程全部都到达了指定点的时候,才能继续往下执行。
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 黑客帝国 设计师:白松林 返回首页