How CyclicBarrier is to become a "fence" of

CyclicBarrierThere is a similar fence, meaning that before you open the fence can only be blocked on one side of the fence, when the fence is removed, before being blocked on one side of the plurality of objects is simultaneously started to move up.

1. How to use CyclicBarrier

  Before introducing the principle, first look at CyclicBarrierhow it should be used.

  Suppose now that there is such a scenario, we need to open a meeting , you need Zhang 1, 2 Zhang, Zhang 3 three people to attend,
the meeting after three people were in attendance to begin, or only dry waiting; this scene with CyclicBarriercan be very fit simulated. code show as below:

public static void main(String[] args) {
    // 线程池,每个线程代表一个人
    ThreadPoolExecutor executor = ThreadPoolProvider.getInstance();
    // 会议所需的人数为3
    CyclicBarrier barrier = new CyclicBarrier(3);

    executor.execute(() -> {
        try {
            System.err.println("张1到达会议室");
            barrier.await();
            System.err.println("会议开始,张1开始发言");
        } catch (InterruptedException | BrokenBarrierException e) {
            e.printStackTrace();
        }

    });

    executor.execute(() -> {
        try {
            System.err.println("张2到达会议室");
            barrier.await();
            System.err.println("会议开始,张2开始发言");
        } catch (InterruptedException | BrokenBarrierException e) {
            e.printStackTrace();
        }

    });

    executor.execute(() -> {
        try {
            System.err.println("张3先去个厕所,内急解决再去开会");
            TimeUnit.SECONDS.sleep(1);
            System.err.println("张3到达会议室");
            barrier.await();
            System.err.println("会议开始,张3开始发言");
        } catch (InterruptedException | BrokenBarrierException e) {
            e.printStackTrace();
        }

    });


    executor.shutdown();
}

Results Figure:
Example of FIG.
  through the top of the code may know CyclicBarriera few points:

  1. Use await()to indicate completion of certain things. (Example above performance reached the conference room )
  2. Use await()after the current thread enters the blocked state , need to wait to fully meet CyclicBarrierthe conditions of the wake to continue the next operation. (In the example above three individuals have reached the conference room )
  3. After the last thread to achieve the condition before blocking all open threads continue to the next operation. (The above example is Zhang reach 3 conference room)

  This simple example also let us know CyclicBarrierusing the method, and that is to look at its internal exactly how to achieve the effect of the fence.

2. CyclicBarrier How is a "fence" of

  We can see from the first code, we need to focus on two places

  1. Constructor
  2. await () method

As long as these two methods to understand the internal, which is equivalent to understand the CyclicBarrierinterior.
Before delving into that, first look at CyclicBarrierseveral variables, do not have to remember, when looking at the code to know what to do with this thing on the line:

lock:CyclicBarrier class creates the ReentrantLockinstance, about ReentrantLockunclear can -> transfer.

trip:lock in condition, CyclicBarrieruse the variable to achieve blocking and wake up at the same time among the threads . Similarly, no clear conditionaction => Portal .

parties: the need to satisfy conditions (call awaitthe number of ways), and that is when there are parties threads await () wakes up all the threads.

barrierCommand: a Runnablevariable in the awaittotal number of calls to the method of arriving partiesafter, before the wake all the threads execute its run()method

generation: its inner classes can be understood as a period, complete the cycle n tasks, as long as a task fails, all of the tasks of the current period even if the failure to end the current cycle, and then turn the next cycle.

count: the current period the number of tasks remaining to be done (the remaining call awaitnumber method)

The following is the source:

public class CyclicBarrier {
    // 内部类,可理解为周期
    private static class Generation {
        // 当前周期是否失败
        boolean broken = false;
    }

    // 锁的实例
    private final ReentrantLock lock = new ReentrantLock();
    // ReentrantLock的condition变量,用来控制线程唤醒和阻塞
    private final Condition trip = lock.newCondition();
    // 需要满足条件的次数,即需要调用await方法的次数
    private final int parties;
    // 满足条件次数达到parties之后,唤醒所有线程之前执行其 run()方法
    private final Runnable barrierCommand;
    // 当前周期
    private Generation generation = new Generation();
    // 剩余满足条件次数
    private int count;
    
    // ...
}

  Read CyclicBarrierafter several variables, look at its internal implementation details.

  First look constructor , which has two constructors, a wake-up directly after reaching the total number of all threads (parties) condition ; another designated a Runnablefirst execute after reaching its run conditions Total () method and then wake up.

  • Do not specify Runnable, only one parameter: the need to reach a number of tasks
public CyclicBarrier(int parties) {
    // 直接调用另一个构造方法,Runnable传null,表示不执行
    this(parties, null);
}
  • Specifies the Runnableconstructor, assignment total number of tasks, the number of remaining tasks, wake up before operationRunnable
public CyclicBarrier(int parties, Runnable barrierAction) {
    if (parties <= 0) throw new IllegalArgumentException();
    // 任务总数
    this.parties = parties;
    // 剩余需要完成的任务数
    this.count = parties;
    // 唤醒之前执行的Runnable
    this.barrierCommand = barrierAction;
}

  In the first section we use the first constructor, to try the second

public static void main(String[] args) throws InterruptedException {

    ThreadPoolExecutor executor = ThreadPoolProvider.getInstance();
    /** =======增加Runnable,其他地方保持一致=============*/
    CyclicBarrier barrier = new CyclicBarrier(3, ()-> System.err.println("在会议开始之前,先给大家发下开会资料"));

    executor.execute(() -> {
        try {
            System.err.println("张1到达会议室");
            barrier.await();
            System.err.println("会议开始,张1开始发言");
        } catch (InterruptedException | BrokenBarrierException e) {
            e.printStackTrace();
        }

    });

    executor.execute(() -> {
        try {
            System.err.println("张2到达会议室");
            barrier.await();
            System.err.println("会议开始,张2开始发言");
        } catch (InterruptedException | BrokenBarrierException e) {
            e.printStackTrace();
        }

    });

    executor.execute(() -> {
        try {
            System.err.println("张3先去个厕所,内急解决再去开会");
            TimeUnit.SECONDS.sleep(1);
            System.err.println("张3到达会议室");
            barrier.await();
            System.err.println("会议开始,张3开始发言");
        } catch (InterruptedException | BrokenBarrierException e) {
            e.printStackTrace();
        }

    });


    executor.shutdown();
}

Results Figure:

Pic2

 Reading the constructor, even if appreciated half CyclicBarrier, the other half of the next term - ; await()tracking code, this is seen

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

Call direct dowaitmethod for the mass participation falseand 0 , which means unlimited waiting, unless the thread is interrupted or wake . Re-entry dowaitmethod, which is the CyclicBarrierother half, in the code below clearly written in the entire execution process

/** 参数说明, timed:是否限时, nanos:限时时间*/
private int dowait(boolean timed, long nanos)
        throws InterruptedException, BrokenBarrierException, TimeoutException {
    // 锁
    final ReentrantLock lock = this.lock;
    // 获取锁,如果失败的话线程睡眠,进入同步队列(AQS中的知识)
    lock.lock();
    try {
        /* 拿到锁之后进入代码处理逻辑*/
        
        // 当前周期
        final Generation g = generation;

        // 如果当前周期是失败的,那么直接抛错
        if (g.broken)
            throw new BrokenBarrierException();

        // 如果当前线程被打断了,那么此次周期失败,设置相关参数,然后抛错
        if (Thread.interrupted()) {
            // 实现代码在下行的注释中,设置相关参数来提醒其他线程周期失败了
            breakBarrier();
            /*
             * private void breakBarrier() {
             *     generation.broken = true;
             *     count = parties;
             *     // 唤醒condition中的所有线程
             *     trip.signalAll();
             * }
             */
            throw new InterruptedException();
        }

        // 如果成功了,那么剩余任务数(count)减1
        int index = --count;
        // 如果为0则表示达到剩余的任务数没有了,达到CyclicBarrier的条件总数了,需要唤醒其他线程
        if (index == 0) {  
            boolean ranAction = false;
            try {
                // 唤醒之前的Runnable
                final Runnable command = barrierCommand;
                // 如果不为空的话执行其run方法
                if (command != null)
                    command.run();
                ranAction = true;
                // 开启下个周期,这个方法是CyclicBarrier可以复用的原因,具体实现在下行注释
                nextGeneration();
                /* private void nextGeneration() {
                 *     // 首先叫醒当前周期的其他线程,告诉其周期结束了,可以执行接下来的操作了
                 *     trip.signalAll();
                 *     // 然后开启下个周期,剩余任务数重置
                 *     count = parties;
                 *     // 下个周期
                 *     generation = new Generation();
                 * }
                 */
                return 0;
            } finally {
                if (!ranAction)
                    breakBarrier();
            }
        }

        // 如果还不能结束本周期,就一直等待直到结束或者周期失败
        for (;;) {
            try {
                // await的过程中是释放锁的
                // 不限时的话就一直等待直到被唤醒或者打断
                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();

            if (g != generation)
                return index;

            if (timed && nanos <= 0L) {
                breakBarrier();
                throw new TimeoutException();
            }
        }
    } finally {
        // 释放锁
        lock.unlock();
    }
}

  Here on the basic understanding of CyclicBarrierthe internal realized, like other arguments awaitit is the same logic, but is more limited access only.

  In fact, if you know ReentrantLock, then you know that CyclicBarrierthe whole is on ReentrantLockthe conditionutilization of it.

3. Summary

  Overall CyclicBarrierimplementation is relatively simple, said that ReentrantLockthe conditionupgraded version is not excessive. The key points of two, for a constructor, before deciding the number of tasks and wake-up operations ; Another point awaitmethod, each time under normal circumstances awaitwould reduce a number of tasks ( the total number determined by the constructor ), the number of tasks time becomes zero indicates the end of the cycle , need to wake up conditionother threads, but on the way encounter failure, then the current cycle fails, wake up other threads together throwing error.



Failure does not make you weak, fear of failure will be.

Guess you like

Origin www.cnblogs.com/zhangweicheng/p/12668921.html