Java CyclicBarrie学习笔记

CyclicBarrier顾名思义回环栅栏,是Java并发库中的一个类。栅栏的意思是可以把多个线程拦住,先到的线程必须等待后面的线程全都到达,然后所有线程同时往下走。回环的意思是,每次拦截之后,如果你还想拦截下一批线程,无需手动初始化就可以直接使用。

例如有几个朋友约定一起到一个饭店吃饭,必须所有人都到达之后,由最后一个人点菜,然后开始吃。

public class CyclicBarrieTest {
    // 自定义工作线程
    private static class Friend extends Thread {

        private CyclicBarrier cyclicBarrier;

        public Friend(CyclicBarrier cyclicBarrier, String name) {
            super(name);
            this.cyclicBarrier = cyclicBarrier;
        }

        @Override
        public void run() {
            super.run();

            try {
                System.out.println(Thread.currentThread().getName() + "开始等待其他朋友");
                cyclicBarrier.await();
                System.out.println(Thread.currentThread().getName() + "开始吃饭");
                // 朋友开始处理,这里用Thread.sleep()来模拟业务处理
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName() + "吃饭完毕");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        int threadCount = 3;
        CyclicBarrier cyclicBarrier = new CyclicBarrier(threadCount, () -> {
            System.out.println("点菜");
        });

        for (int i = 0; i < threadCount; i++) {
            System.out.println("约好朋友" + i);
            Friend worker = new Friend(cyclicBarrier, "朋友" + i);
            worker.start();
        }
    }

}

运行之:

约好朋友0
约好朋友1
约好朋友2
朋友1开始等待其他朋友
朋友0开始等待其他朋友
朋友2开始等待其他朋友
点菜
朋友2开始吃饭
朋友1开始吃饭
朋友0开始吃饭
朋友1吃饭完毕
朋友0吃饭完毕
朋友2吃饭完毕

可以看出先是朋友之间约好,然后三个朋友依次到达饭店,先到的必须等到最后一个到达的,最后一个到达之后点菜。点完之后三个朋友同时开始吃,但有的吃的快,有的慢,最后都吃完,程序也就运行结束了。

下面分析一下关键的代码,首先CyclicBarrie初始化时,可以传入一个整型变量parties,以及一个Runnable变量barrieAction(可选)。parties代表可以有几个线程等待,barrieAction是可选的,代表最后一个线程到达之后,在最后一个线程中运行barrieAction。

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

这里还有一个count,count代表当前还有几个线程在等待。

await方法代表执行线程已经到达,开始等待其他线程。

    public int await() throws InterruptedException, BrokenBarrierException {
        try {
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
            throw new Error(toe); // cannot happen
        }
    }

底层调用了doAwait方法

    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();
            }

            // 将等待的线程数减1
            int index = --count;
            
            // 最后一个线程到达
            if (index == 0) {  // tripped
                // 执行所有线程到达之后的指定任务
                Runnable command = barrierCommand;
                if (command != null) {
                    try {
                        command.run();
                    } catch (Throwable ex) {
                        breakBarrier();
                        throw ex;
                    }
                }

                // 唤醒所有等待线程执行,同时重新初始化栅栏
                nextGeneration();
                return 0;
            }

            // loop until tripped, broken, interrupted, or timed out
            // 不是最后一个到达的线程,则循环等待打破循环
            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 {
                        // 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();

                // 如果已经被其他线程重新初始化,则返回index
                if (g != generation)
                    return index;

                // 如果超过等待时间,则打破栅栏,并抛异常
                if (timed && nanos <= 0L) {
                    breakBarrier();
                    throw new TimeoutException();
                }
            }
        } finally {
            lock.unlock();
        }
    }

再看看打破栅栏:

    /**
     * Sets current barrier generation as broken and wakes up everyone.
     * Called only while holding lock.
     * 设置栅栏为已打破状态,将等待线程恢复为parties,同时通知所有等待线程恢复执行
     */
    private void breakBarrier() {
        generation.broken = true;
        count = parties;
        trip.signalAll();
    }

再看看nextGeneration()方法:

    // 通知所有等待线程恢复执行,将等待线程数恢复为parties,初始化一个新的Generation,代表开启一个新的等待栅栏
    private void nextGeneration() {
        // signal completion of last generation
        trip.signalAll();
        // set up next generation
        count = parties;
        generation = new Generation();
    }

以上就是主要的代码分析,其实还是比较简单的。仔细想想就能看明白

你可能感兴趣的:(Java基础,java,学习,多线程)