CyclicBarrier理解

  CyclicBarrier字面意思是“循环的屏障”。实际效果是多个线程完成后会到达这个屏障,令线程阻塞,直到所有的线程都完成后,再唤醒所有线程。那为什么叫“循环的”呢?因为这个类可以重用。关于重用,我们等下可以再源代码中看到。通过源代码就可以理解重用的含义了。

private static class Generation {
        boolean broken = false;
    }

    /** The lock for guarding barrier entry */
    private final ReentrantLock lock = new ReentrantLock();
    /** Condition to wait on until tripped */
    private final Condition trip = lock.newCondition();
    /** The number of parties */
    private final int parties;
    /* The command to run when tripped */
    private final Runnable barrierCommand;
    /** The current generation */
    private Generation generation = new Generation();

    /**
     * Number of parties still waiting. Counts down from parties to 0
     * on each generation.  It is reset to parties on each new
     * generation or when broken.
     */
    private int count;

这是CyclicBarrier这个类的所有属性,在这里说明一下,Generation这个内部类,代表的就是屏障,他有一个broken属性,用来标识这个屏障是否被打破。lock,trip就是之前说过的重入锁和Condition。因此我们知道CyclicBarrier底层其实是通过ReentrantLock来实现的。parties用来标识线程的数量,barrierCommand是当所有得线程都到达屏障之后执行的操作。count表示还没有到达屏障的数量。

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

这个类有两个构造方法,一种是带有barrierAction参数的。一种是不带的。这个barrierAction其实就是当多个线程都到达屏障之后继续执行的操作。

public int await() throws InterruptedException, BrokenBarrierException {
        try {
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
            throw new Error(toe); // cannot happen
        }
    }
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;
// 如果所有的线程都到达了屏障
if (index == 0) { // tripped boolean ranAction = false; try { final Runnable command = barrierCommand; if (command != null)
// 执行barrierCommand command.run(); ranAction
= true;
//这一代结束,生成下一代 nextGeneration();
return 0; } finally { if (!ranAction)
// 如果barrierCommand执行失败,打破屏障 breakBarrier(); } }
// loop until tripped, broken, interrupted, or timed out for (;;) { try {
// 通过Condition来让线程阻塞
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(); } }
private void nextGeneration() {
        // signal completion of last generation
        trip.signalAll();
        // set up next generation
        count = parties;
        generation = new Generation();
    } 
   
 private void breakBarrier() {
        generation.broken = true;
        count = parties;
        trip.signalAll();
    }

  整个类的方法还是比较简单的,通过代码我们就基本上已经知道怎么使用了,先通过构造方法指定线程数量,以及都达到屏障之后要执行的方法,然后线程完成之后调用await()方法,当所有的线程都调用await()方法后,就会唤醒所有线程,然后执行barrierCommand,并且生成下一代。之前说的重用就是因为会生成新的一代,因此可以重用。

posted @ 2018-12-04 17:39  徐世龙  阅读(1718)  评论(0编辑  收藏  举报