CyclicBarrier基础和原理

什么是CyclicBarrier?

CyclicBarrierJUC中的一个同步辅助类 ,它允许一组线程互相等待 ,直到所有线程都到达某个公共屏障点后,这些线程才会继续执行。

CountDownLatch⾮常类似,它也可以实现线程间的计数等待,但它的功能⽐CountDownLatch更加复杂且强⼤。

使用示例

java 复制代码
public static void  main(String[] args) throws ExecutionException, InterruptedException, NoSuchFieldException, IllegalAccessException {
        int threadCount=5;
        CyclicBarrier cyclicBarrier=new CyclicBarrier(threadCount,()->{
            int sleepTime=5;
           System.out.println("所有线程已经就位,开始执行下一阶段的任务");
            try {
                for(int i=sleepTime;i>0;i--){
                    System.out.println("任务开始倒计时:"+i);

                    TimeUnit.SECONDS.sleep(1);
                }

            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        for(int i=0;i<5;i++){
            String name="worker-"+ i;
            new Thread(()->{
                try {
                    System.out.println(name + " 正在执行第一阶段任务...");
                    Thread.sleep((long) (Math.random() * 1000));
                    System.out.println(name + " 已完成第一阶段任务,等待其他线程...");
                    // 等待其他线程。当所有线程都调用 await() 后,继续执行。
                    int arrivalIndex = cyclicBarrier.await(); // 返回值表示是第几个到达的(从0开始)
                    // 第二阶段任务(所有线程同时开始)
                    System.out.println(name + " 开始执行第二阶段任务...(我是第" + arrivalIndex + "个到达的)");
                    Thread.sleep((long) (Math.random() * 1000));
                    System.out.println(name + " 已完成第二阶段任务。");
                } catch (InterruptedException | BrokenBarrierException e) {
                    throw new RuntimeException(e);
                }
            }).start();
        }
    }

CyclicBarrier原理

构造方法

java 复制代码
    public CyclicBarrier(int parties, Runnable barrierAction) {
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties;
        this.count = parties;
        this.barrierCommand = barrierAction;
    }
    public CyclicBarrier(int parties) {
        this(parties, null);
    }

构造方法只是将变量赋给了自己的数据域中的变量。

await原理

java 复制代码
    public int await() throws InterruptedException, BrokenBarrierException {
        try {
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
            throw new Error(toe); // cannot happen
        }
    }
    public int await(long timeout, TimeUnit unit)
        throws InterruptedException,
               BrokenBarrierException,
               TimeoutException {
        return dowait(true, unit.toNanos(timeout));
    }

本质上底层都是调用了doawait这个方法。

java 复制代码
    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();
            }
			//当前线程执行到了await,因此 任务数量线程-1
            int index = --count;
            //如果最后一个完成
            if (index == 0) {
                //最后一个完成任务的线程,还需要运行 构造方法传入的Runnable任务
                Runnable command = barrierCommand;
                if (command != null) {
                    try {
                        command.run();
                    } catch (Throwable ex) {
                        breakBarrier();
                        throw ex;
                    }
                }
                //进行下一代任务,并唤醒所有的线程
                nextGeneration();
                return 0;
            }

            //如果不是最后一个完成的线程
            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();
        }
    }

总结上诉的流程:

  1. 加独占锁

  2. 记录目前的"代",并检测之前的线程有没有破坏屏障,再检测当前线程有没有被中断,如果中断需要破坏屏障。

  3. 扣除等待线程数量

  4. 如果当前线程是最后一个到达的线程

    • 它要执行传入的Runnable任务
    • 它要创建新的"代",并唤醒所有线程
  5. 如果当前线程不是最后一个到达的线程

    • 它要等待
  6. 线程被唤醒后,检测屏障有没有被破坏,并检测有没有进入下一代,如果是下一代了,返回到达的顺序

可以看出上面的流程总是出现了 以及屏障,那么这究竟是什么?

Generation

java 复制代码
    private static class Generation {
        Generation() {}                 // prevent access constructor creation
        boolean broken;                 // initially false
    }

它的作用是为CyclicBarrier每一轮协作周期 提供一个独立的状态容器CyclicBarrier是可循环使用的。Generation对象代表了"一轮"或"一代"完整的等待-冲破屏障的过程。 每次屏障被冲破后,或者当屏障被重置(reset())时,CyclicBarrier内部会创建一个新的Generation实例,标志着新一轮协作的开始。

核心字段broken的作用:

  • 这是一个 boolean标志,初始值为 false
  • broken = false :表示当前这一"代"的屏障是完好有效的。所有线程都在正常等待或已成功冲破屏障。
  • broken = true :表示当前这一"代"的屏障已被破坏 。这通常发生在某个等待线程被中断(InterruptedException)、超时,或者有线程调用了 reset()方法。
  • 关键作用 :一旦某一"代"的屏障被标记为 broken所有在当前代中尚未被唤醒的、仍在等待的线程 ,都会立即抛出 BrokenBarrierException。这确保了状态的一致性,避免了线程永远等待在一个已经失效的屏障上。

breakBarrier

java 复制代码
    private void breakBarrier() {
        generation.broken = true;
        count = parties;
        trip.signalAll();
    }

将当前"代"标记为已破坏,同时重置未到达的线程数count,重置计数器使得屏障可以立即进入"可重用 "状态,最终调用signalAll唤醒所有的已经到达并且阻塞的线程。

nextGeneration

java 复制代码
    private void nextGeneration() {
        // signal completion of last generation
        trip.signalAll();
        // set up next generation
        count = parties;
        generation = new Generation();
    }

先唤醒所有的线程,重置未到达的线程数count,创建一个新的"代"。 nextGeneration()是CyclicBarrier可循环特性的实现核心。 它通过"代"的轮换机制,让同一个屏障对象可以安全、清晰地支持多轮同步协作。

相关推荐
赫瑞2 小时前
Java中的图论3 —— Floyd
java·开发语言·图论
菜鸟程序员专写BUG2 小时前
SpringBoot跨域报错全集|CORS、OPTIONS预检、无Access-Control报错全解决
spring boot·后端·状态模式
程序员小寒2 小时前
JavaScript设计模式(六):职责链模式实现与应用
java·javascript·设计模式
关于不上作者榜就原神启动那件事2 小时前
@Transactional事务失效总结
java·开发语言·jvm
jaysee-sjc2 小时前
【项目三】用GUI编程实现局域网群聊软件
java·开发语言·算法·安全·intellij-idea
无名-CODING2 小时前
Java 爬虫高级技术:反反爬策略与分布式爬虫实战
java·分布式·爬虫
jonyleek2 小时前
JVS物联网应用中控制器的四大职责和设备接入全流程
java·struts·servlet·私有化部署
csdn2015_2 小时前
java 将 List<Map<String,Object>> 类型里面的值转换为List<String>
java·windows·list
怀化纱厂球迷2 小时前
android车载应用动画-仿窗帘式下拉显示!Android 实现跟手裁剪动画 + RecyclerView 列表展示
android·java