J.U.C 之CyclicBarrier

Brief introduction

CyclicBarrier, a synchronization aid in AP I is so described:

It allows a set of threads waiting for each other to reach a common barrier point (Common Barrier Point). Relates to a set of program threads fixed size, these threads must wait for each other from time to time, this time CyclicBarrier useful. Because the Barrier can be reused after the release waiting threads, so called cycle (Cyclic) barrier (Barrier).

Popular speak is this: let a group of threads are blocked when a barrier arrival until the last thread reaches the barrier, the barrier will open the door, all barriers blocked thread will continue to work.

Achieve analysis

java.util.concurrent.CyclicBarrier following structure:

By the map, we can see the internal CyclicBarrier is to use a reentrant lock ReentrantLock and Condition.

It has two constructors:

  1. CyclicBarrier (int parties): create a new CyclicBarrier, it will start at a given number of parties (threads) in a wait state, but it does not perform a predefined action at startup barrier.

  2. CyclicBarrier (int parties, Runnable barrierAction): create a new CyclicBarrier, it will start at a given number of parties (threads) in a wait state, and perform a given barrier action when starting barrier, which is operated by a final into the barrier of thread.

code show as below:

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);
}
复制代码
parties 变量,表示拦截线程的总数量。
count 变量,表示拦截线程的剩余需要数量。
barrierAction 变量,为 CyclicBarrier 接收的 Runnable 命令,用于在线程到达屏障时,优先执行 barrierAction ,用于处理更加复杂的业务场景。
generation 变量,表示 CyclicBarrier 的更新换代。详细解析,见 「2.4 Generation」 。
复制代码

await

Each thread calls #await () method, I tell CyclicBarrier have reached the barrier, then the current thread is blocked. When all threads have reached the barrier, blocking the end, all threads can continue to follow the logic.

public int await() throws InterruptedException, BrokenBarrierException {
    try {
        return dowait(false, 0L);//不超时等待
    } catch (TimeoutException toe) {
        throw new Error(toe); // cannot happen
    }
}
复制代码

Internal call #dowait (boolean timed, long nanos) method, to perform blocking wait (timed = true).

Theory, TimeoutException not appear abnormal, so in the event of a direct throw Error error.

the arrival index of the current thread, where index {@code getParties() - 1}
indicates the first to arrive and zero indicates the last to arrive
复制代码

await

await (long timeout, TimeUnit unit) method, based on #await () on the additional feature of waiting for the timeout. code show as below:

public int await(long timeout, TimeUnit unit)
    throws InterruptedException,
           BrokenBarrierException,
           TimeoutException {
    return dowait(true, unit.toNanos(timeout));
}
复制代码

Internal call #dowait (boolean timed, long nanos) method, to perform blocking wait (timed = true).

dowait

dowait (boolean timed, long nanos) method, as follows:

private int dowait(boolean timed, long nanos)
        throws InterruptedException, BrokenBarrierException,
        TimeoutException {
    //获取锁
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        //分代
        final Generation g = generation;

        //当前generation“已损坏”,抛出BrokenBarrierException异常
        //抛出该异常一般都是某个线程在等待某个处于“断开”状态的CyclicBarrie
        if (g.broken)
            //当某个线程试图等待处于断开状态的 barrier 时,或者 barrier 进入断开状态而线程处于等待状态时,抛出该异常
            throw new BrokenBarrierException();

        //如果线程中断,终止CyclicBarrier
        if (Thread.interrupted()) {
            breakBarrier();
            throw new InterruptedException();
        }

        //进来一个线程 count - 1
        int index = --count;
        //count == 0 表示所有线程均已到位,触发Runnable任务
        if (index == 0) {  // tripped
            boolean ranAction = false;
            try {
                final Runnable command = barrierCommand;
                //触发任务
                if (command != null)
                    command.run();
                ranAction = true;
                //唤醒所有等待线程,并更新generation
                nextGeneration();
                return 0;
            } finally {
                if (!ranAction) // 未执行,说明 barrierCommand 执行报错,或者线程打断等等情况。
                    breakBarrier();
            }
        }

        for (;;) {
            try {
                //如果不是超时等待,则调用Condition.await()方法等待
                if (!timed)
                    trip.await();
                else if (nanos > 0L)
                    //超时等待,调用Condition.awaitNanos()方法等待
                    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();

            //generation已经更新,返回index
            if (g != generation)
                return index;

            //“超时等待”,并且时间已到,终止CyclicBarrier,并抛出异常
            if (timed && nanos <= 0L) {
                breakBarrier();
                throw new TimeoutException();
            }
        }
    } finally {
        //释放锁
        lock.unlock();
    }
}
复制代码

If the thread is not the arrival of the last thread, then he would have been in a wait state, unless the following occurs:

  1. The last thread reaches that index == 0.
  2. Beyond the specified time (timeout wait).
  3. Some other thread interrupts the current thread.
  4. Some other thread interrupts another thread to wait.
  5. Some other thread is waiting for a timeout barrier.
  6. Some other thread calls this barrier #reset () method. #reset () method, used to reset the barrier to its initial state.

In the source code #dowait (boolean timed, long nanos) method, we can always see BrokenBarrierException is thrown, then throw an exception when it? E.g:

如果一个线程处于等待状态时,如果其他线程调用 #reset() 方法。
调用的 barrier 原本就是被损坏的,则抛出 BrokenBarrierException 异常。
任何线程在等待时被中断了,则其他所有线程都将抛出 BrokenBarrierException 异常,并将 barrier 置于损坏状态。
复制代码

Generation

Generation is CyclicBarrier internal static class, describes the replacement of CyclicBarrier. In CyclicBarrier, the same number of threads belong to the same generation. When parties reach all threads barrier, generation will be upgrading. Where the broken property identifies the current CyclicBarrier is already in a broken state. code show as below:

private static class Generation {
    boolean broken = false;
}
复制代码

The default barrier is not damaged.

breakBarrier

When the barrier is damaged, or there is a thread interrupted, through #breakBarrier () method to terminate all the threads. code show as below:

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

In breakBarrier () method, in addition to the broken set to true, also calls #signalAll () method, the thread will be in a wait state in the wake CyclicBarrier all.

nextGeneration

When all threads have reached the barrier (index == 0), will by NextGeneration () method, the replacement operation. In this step, we do three things:

  1. Wake up all the threads.
  2. Reset count.
  3. Reset generation.

code show as below:

private void nextGeneration() {
    trip.signalAll();
    count = parties;
    generation = new Generation();
}
复制代码

reset

reset () method, barrier reset to the initial state. code show as below:

public void reset() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        breakBarrier();   // break the current generation
        nextGeneration(); // start a new generation
    } finally {
        lock.unlock();
    }
}
复制代码

By a combination of #breakBarrier () and #nextGeneration () method.

getNumberWaiting

getNumberWaiting () method to obtain the number of threads waiting. code show as below:

public int getNumberWaiting() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        return parties - count;
    } finally {
        lock.unlock();
    }
}
复制代码

isBroken

isBroken () method to determine whether CyclicBarrier in break. code show as below:

public boolean isBroken() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        return generation.broken;
    } finally {
        lock.unlock();
    }
}
复制代码

Scenarios

CyclicBarrier combined result as multi-threaded operation, for multi-threaded computing the data, and merge the results of the scenarios. For example, we need more statistical data in Excel, and then wait for a total result. We can handle multi-threaded through each Excel, after the completion of the implementation of the corresponding results, to calculate the final results of these threads through barrierAction, to obtain the sum of all of Excel.

Application Examples

For example, we only wait to meet all the people in attendance will be a meeting, as follows:

public class CyclicBarrierTest {

    private static CyclicBarrier cyclicBarrier;

    static class CyclicBarrierThread extends Thread{
        public void run() {
            System.out.println(Thread.currentThread().getName() + "到了");
            //等待
            try {
                cyclicBarrier.await();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args){
        cyclicBarrier = new CyclicBarrier(5, new Runnable() {
            @Override
            public void run() {
                System.out.println("人到齐了,开会吧....");
            }
        });

        for(int i = 0 ; i < 5 ; i++){
            new CyclicBarrierThread().start();
        }
    }
    
}
复制代码

operation result:

Guess you like

Origin juejin.im/post/5d8b06a5e51d45781c6fccb8