CyclicBarrier 使用与源码解析

76 阅读2分钟
栅栏是JUC中提供的一个工具包,用于构建一个批量线程执行的场景。

使用示例

public static void main(String[] args) throws InterruptedException {
        CyclicBarrier barrier = new CyclicBarrier(3, () -> {
            System.out.println("处理完成一批。");
        });

        List<Supplier<String>> tasks = new LinkedList<>();
        // 初始化假定任务
        for (int i = 0; i < 12; i++) {
            final int j = i;
            tasks.add(() -> {
                try {
                    TimeUnit.SECONDS.sleep(j);// 设定假定等待请求的时间
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                try {
                    barrier.await();
                } catch (InterruptedException | BrokenBarrierException e) {
                    throw new RuntimeException(e);
                }
                log.info("[{}] task -> {} <- exec", Thread.currentThread().getName(), j + " ");
                return j + " ";
            });
        }

        // 执行任务线程
        List<Thread> threads = new ArrayList<>(tasks.size());
        for (Supplier<String> task : tasks) {
            threads.add(new Thread(task::get));
        }

        for (Thread thread : threads) {
            thread.start();
        }

        for (Thread thread : threads) {
            thread.join();
        }
    }

源码介绍

关键的栅栏属性

// 每个栅栏的实例
private static class Generation {  
Generation() {} // prevent access constructor creation  
	boolean broken; // initially false   如果未TRUE说明栅栏被放开
}  
  
/** 实现栅栏同步的锁 */  
private final ReentrantLock lock = new ReentrantLock();   
/** 等待条件对象 */  
private final Condition trip = lock.newCondition();  
/** 需要等待的数量 */  
private final int parties;  
/** 栅栏放开时执行的方法 */  
private final Runnable barrierCommand;  
/** The current generation */  
private Generation generation = new Generation();  
  
/**  
*  当前栅栏中剩余等待数量
*/  
private int count;

关键方法dowait()

栅栏中主要的方法就是dowait,包含线程的阻塞唤醒等逻辑。
/**
     * Main barrier code, covering the various policies.
     */
    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) // 如果当前的Barrier 已经结束,则直接抛出异常
                throw new BrokenBarrierException();

            if (Thread.interrupted()) { // 如果线程被中断则直接关闭Barrier并抛出异常
                breakBarrier();
                throw new InterruptedException();
            }

            int index = --count; // 等待数量减一,判断已经到达多少线程
            if (index == 0) {  // tripped Barrier 放开。允许线程通过,并且执行配置的后置执行任务
                Runnable command = barrierCommand;
                if (command != null) {
                    try {
                        command.run();
                    } catch (Throwable ex) {
                        breakBarrier(); // 1、放开栅栏(Barrier); 2、唤醒所有线程
                        throw ex;
                    }
                }
                nextGeneration(); // 1、通知所有等待线程; 2、开启一个新的栅栏;
                return 0;
            }

            // loop until tripped, broken, interrupted, or timed out
            for (;;) {
                try {
                    if (!timed) // 如果没有超时机制则直接调用Condition.await()等待被唤醒
                        trip.await();
                    else if (nanos > 0L) // 如果配置了超时机制则添加超时时间等待
                        nanos = trip.awaitNanos(nanos);
                } catch (InterruptedException ie) {
                    /*
                        这里判断generation对象是否是线程进入栅栏的第一次,如果是第一次并且没有被放开则放开栅栏并抛出异常.
                        e.g. 线程如果在等待的过程中发生了中断异常,则放开栅栏,并唤醒所有其他线程。
                     */
                    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();
        }
    }