CyclicBarrier 解读
Posted ronnieyuan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CyclicBarrier 解读相关的知识,希望对你有一定的参考价值。
简介
字面上的意思: 可循环利用的屏障。
作用: 让所有线程都等待完成后再继续下一步行动。
举例模拟: 吃饭人没到齐不准动筷。
使用Demo
package com.ronnie;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo {
static class TaskThread extends Thread {
CyclicBarrier barrier;
TaskThread(CyclicBarrier barrier){
this.barrier = barrier;
}
@Override
public void run() {
try {
Thread.sleep(1000);
// getName() 是来自Thread类的一个被final修饰的方法, 用于获取当前线程名
System.out.println(getName() + " attached barrier A");
barrier.await();
System.out.println(getName() + " breaks barrier A");
Thread.sleep(2000);
System.out.println(getName() + " attached barrier B");
barrier.await();
System.out.println(getName() + " breaks barrier B");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
int threadNum = 5;
CyclicBarrier barrier = new CyclicBarrier(threadNum, new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " finished the last task");
}
});
for (int i = 0; i < threadNum; i++){
new TaskThread(barrier).start();
}
}
}
执行结果
Thread-3 attached barrier A Thread-4 attached barrier A Thread-0 attached barrier A Thread-1 attached barrier A Thread-2 attached barrier A Thread-2 finished the last task Thread-2 breaks barrier A Thread-0 breaks barrier A Thread-3 breaks barrier A Thread-1 breaks barrier A Thread-4 breaks barrier A Thread-2 attached barrier B Thread-3 attached barrier B Thread-4 attached barrier B Thread-0 attached barrier B Thread-1 attached barrier B Thread-1 finished the last task Thread-1 breaks barrier B Thread-2 breaks barrier B Thread-3 breaks barrier B Thread-0 breaks barrier B Thread-4 breaks barrier B
可以看到0~4号线程在到达障碍A, B后都会等待其他线程完成任务再突破障碍
源码解读
类注释
/** * A synchronization aid that allows a set of threads to all wait for * each other to reach a common barrier point. * 一允许一组线程集合都等待其他线程到达一个普通障碍点的同步助手。 * CyclicBarriers are useful in programs involving a fixed sized party of * threads that must occasionally wait for each other. * CyclicBarriers 在与一组数量不变且必须偶尔等待其他线程的线程组相关的编程中比较有用。 * The barrier is called <em>cyclic</em> because it can be re-used after the * waiting threads are released. * 该障碍被称为可循环是因为在等待线程被释放后, 此障碍是可重用的。 * * <p>A {@code CyclicBarrier} supports an optional {@link Runnable} command * that is run once per barrier point, after the last thread in the party * arrives, but before any threads are released. * 一个CyclicBarrier支持一个可选的命令, 它在每个障碍点运行一次, 运行在线程组中最后一个 * 线程到达后但其他线程没有被释放之前。 * * This <em>barrier action</em> is useful * for updating shared-state before any of the parties continue. * 此障碍行为在任意线程组继续执行之前进行更新共享状态比较有效。 * * <p><b>Sample usage:</b> Here is an example of using a barrier in a * parallel decomposition design: * 一下为一个并行分解设计中的CyclicBarrier的使用。 * * <pre> {@code * class Solver { * final int N; * final float[][] data; * final CyclicBarrier barrier; * * class Worker implements Runnable { * int myRow; * Worker(int row) { myRow = row; } * public void run() { * while (!done()) { * processRow(myRow); * * try { * barrier.await(); * } catch (InterruptedException ex) { * return; * } catch (BrokenBarrierException ex) { * return; * } * } * } * } * * public Solver(float[][] matrix) { * data = matrix; * N = matrix.length; * Runnable barrierAction = * new Runnable() { public void run() { mergeRows(...); }}; * barrier = new CyclicBarrier(N, barrierAction); * * List<Thread> threads = new ArrayList<Thread>(N); * for (int i = 0; i < N; i++) { * Thread thread = new Thread(new Worker(i)); * threads.add(thread); * thread.start(); * } * * // wait until done * for (Thread thread : threads) * thread.join(); * } * }}</pre> * * Here, each worker thread processes a row of the matrix then waits at the * barrier until all rows have been processed. * 在以上例子中, 每个工作线程都对该矩阵的一行进行操作, 再等待在阻碍处, 直到所有行数据都 * 被处理完。 * * When all rows are processed the supplied {@link Runnable} barrier action is * executed and merges the rows. * 当所有行都被提供的可执行线程执行完毕, 阻碍行为就会被执行并合并行数据。 * * If the merger determines that a solution has been found then {@code done()} 、 * will return {@code true} and each worker will terminate. * 如果合并这去人有解决方案被找到, 那么done()方法就会返回true, 并且每个工作线程都会终 * 止。 * * <p>If the barrier action does not rely on the parties being suspended when * it is executed, then any of the threads in the party could execute that * action when it is released. * 如果阻碍行为不依赖于在执行时被暂缓的线程组, 那么该线程组中的任意线程都可以在其被释放 * 时执行。 * * To facilitate this, each invocation of {@link #await} returns the arrival * index of that thread at the barrier. * 为了遍历, 每次await方法的调用都会返回线程到达障碍的到达参数 * * You can then choose which thread should execute the barrier action, for * example: * 你可以选择哪个线程应该执行障碍行为, 比如: * <pre> {@code * if (barrier.await() == 0) { * // log the completion of this iteration * }}</pre> * * <p>The {@code CyclicBarrier} uses an all-or-none breakage model * for failed synchronization attempts: * CyclicBarrier 为失败的同步尝试使用了一个全部或没有的断开模型。 * * If a thread leaves a barrier point prematurely because of interruption, * failure, or timeout, all other threads waiting at that barrier point will * also leave abnormally via {@link BrokenBarrierException} (or {@link * InterruptedException} if they too were interrupted at about the same time). * 如果一个线程由于打断, 失败或超时过早地离开了阻碍点, 所有其他等待在该阻碍点的线程都会 * 在它们被打断的同时通过非正常的障碍破坏异常或打断异常离开。 * * <p>Memory consistency effects: Actions in a thread prior to calling * {@code await()} * 内存一致性影响: * 线程中的行为优先于调用await()方法 * <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a> * actions that are part of the barrier action, which in turn * <i>happen-before</i> actions following a successful return from the * corresponding {@code await()} in other threads. * 内存可见性(happens-before原则), 线程行为是阻碍行为的一部分, 线程行为伴随着来自其他 * 线程中对应的await方法的成功范回。 * * @since 1.5 * @see CountDownLatch * * @author Doug Lea */
私有静态内部类 Generation(代)
/** * Each use of the barrier is represented as a generation instance. * 每次障碍的使用都用一个代数代表(ps: 0,1,2,3,4...... 第N代) * The generation changes whenever the barrier is tripped, or * is reset. * 此代数每当障碍被触发或者被重设时都会改变。 * There can be many generations associated with threads * using the barrier - due to the non-deterministic way the lock * may be allocated to waiting threads - but only one of these * can be active at a time (the one to which {@code count} applies) * and all the rest are either broken or tripped. * 由于不可抗拒的原因, 锁可能被分配给等待中的线程, 但是其中只有一个能特定时间活跃 * (count计数应用于此), 并且所有其他的的线程会是被破坏状态或是停滞状态。 * There need not be an active generation if there has been a break * but no subsequent reset. * 如果有打断但没有紧接着的重设, 那么久不需要活跃年代。 */ 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. * 对每一代从分区数减至0 * It is reset to parties on each new generation or when broken. * 当新一代产生或者被破坏, 就将count重设为线程组数。 */ private int count;
构造器
/** * Creates a new {@code CyclicBarrier} that will trip when the * given number of parties (threads) are waiting upon it, and which * will execute the given barrier action when the barrier is tripped, * performed by the last thread entering the barrier. * * @param parties the number of threads that must invoke {@link #await} * before the barrier is tripped * @param barrierAction the command to execute when the barrier is * tripped, or {@code null} if there is no action * @throws IllegalArgumentException if {@code parties} is less than 1 */ public CyclicBarrier(int parties, Runnable barrierAction) { if (parties <= 0) throw new IllegalArgumentException(); this.parties = parties; this.count = parties; this.barrierCommand = barrierAction; } /** * Creates a new {@code CyclicBarrier} that will trip when the * given number of parties (threads) are waiting upon it, and * does not perform a predefined action when the barrier is tripped. * 区别就是没有前置定义的行为 * * @param parties the number of threads that must invoke {@link #await} * before the barrier is tripped * @throws IllegalArgumentException if {@code parties} is less than 1 */ public CyclicBarrier(int parties) { this(parties, null); }
方法
await()
/** * Waits until all {@linkplain #getParties parties} have invoked * {@code await} on this barrier. * 等待直至所有组在此阻碍上调用await方法 * * <p>If the current thread is not the last to arrive then it is * disabled for thread scheduling purposes and lies dormant until * one of the following things happens: * 如果当前线程不是最后一个到达的, 那么它就会因线程调度被暂停并一直休眠直至以下 * 事情发生: * <ul> * <li>The last thread arrives; or 最后一个线程到达 * <li>Some other thread {@linkplain Thread#interrupt interrupts} * the current thread; or 其他线程打断了当前线程 * <li>Some other thread {@linkplain Thread#interrupt interrupts} * one of the other waiting threads; or * 其他线程打断了其他等待线程的一个 * <li>Some other thread times out while waiting for barrier; or * 其他线程等待在障碍时超时 * <li>Some other thread invokes {@link #reset} on this barrier. * 其他线程调在障碍上调用reset * </ul> * * <p>If the current thread: * <ul> * <li>has its interrupted status set on entry to this method; or * 如果当前线程有它自己的打断状态设置在该方法的入口或 * <li>is {@linkplain Thread#interrupt interrupted} while waiting * </ul> * then {@link InterruptedException} is thrown and the current thread's * interrupted status is cleared. * 在等待时被打断, 那么就会在线程的打断状态被清除时抛出打断异常。 * * <p>If the barrier is {@link #reset} while any thread is waiting, * or if the barrier {@linkplain #isBroken is broken} when * {@code await} is invoked, or while any thread is waiting, then * {@link BrokenBarrierException} is thrown. * 如果障碍在任何线程处于等待状态时, 或障碍在被请求时已经被破坏, 或任意线程在等 * 待时被重设, 就会抛出障碍破坏异常 * * <p>If any thread is {@linkplain Thread#interrupt interrupted} while * waiting, then all other waiting threads will throw * {@link BrokenBarrierException} and the barrier is placed in the broken * state. * 如果任何线程在等待时被打断, 那么所有其他等待着的线程都会抛出障碍破坏异常, 并 * 且障碍被置于破坏状态。 * * <p>If the current thread is the last thread to arrive, and a * non-null barrier action was supplied in the constructor, then the * current thread runs the action before allowing the other threads to * continue. * 如果当前线程时最后一个到达的线程, 并且一个非空的障碍行为被应用到此构造器, 那 * 么当前线程会在允许其他线程执行前运行此行为(也就是说其他线程会阻塞) * If an exception occurs during the barrier action then that exception * will be propagated in the current thread and the barrier is placed in * the broken state. * 如果在阻碍行为期间发生了异常, 那么该异常将被传播到当前线程中并且张阿偶被置于 * 破坏状态 * * @return the arrival index of the current thread, where index * {@code getParties() - 1} indicates the first * to arrive and zero indicates the last to arrive * 返回当前线程到达参数, 为线程组数 - 1 * @throws InterruptedException if the current thread was interrupted * while waiting * @throws BrokenBarrierException if <em>another</em> thread was * interrupted or timed out while the current thread was * waiting, or the barrier was reset, or the barrier was * broken when {@code await} was called, or the barrier * action (if present) failed due to an exception */ public int await() throws InterruptedException, BrokenBarrierException { try { return dowait(false, 0L); } catch (TimeoutException toe) { throw new Error(toe); // cannot happen } }
await(long timeout, TimeUnit unit)
/** * Waits until all {@linkplain #getParties parties} have invoked * {@code await} on this barrier, or the specified waiting time elapses. * * <p>If the current thread is not the last to arrive then it is * disabled for thread scheduling purposes and lies dormant until * one of the following things happens: * <ul> * <li>The last thread arrives; or * <li>The specified timeout elapses; or * <li>Some other thread {@linkplain Thread#interrupt interrupts} * the current thread; or * <li>Some other thread {@linkplain Thread#interrupt interrupts} * one of the other waiting threads; or * <li>Some other thread times out while waiting for barrier; or * <li>Some other thread invokes {@link #reset} on this barrier. * </ul> * * <p>If the current thread: * <ul> * <li>has its interrupted status set on entry to this method; or * <li>is {@linkplain Thread#interrupt interrupted} while waiting * </ul> * then {@link InterruptedException} is thrown and the current thread's * interrupted status is cleared. * * <p>If the specified waiting time elapses then {@link TimeoutException} * is thrown. If the time is less than or equal to zero, the * method will not wait at all. * * <p>If the barrier is {@link #reset} while any thread is waiting, * or if the barrier {@linkplain #isBroken is broken} when * {@code await} is invoked, or while any thread is waiting, then * {@link BrokenBarrierException} is thrown. * * <p>If any thread is {@linkplain Thread#interrupt interrupted} while * waiting, then all other waiting threads will throw {@link * BrokenBarrierException} and the barrier is placed in the broken * state. * * <p>If the current thread is the last thread to arrive, and a * non-null barrier action was supplied in the constructor, then the * current thread runs the action before allowing the other threads to * continue. * If an exception occurs during the barrier action then that exception * will be propagated in the current thread and the barrier is placed in * the broken state. * * @param timeout the time to wait for the barrier * @param unit the time unit of the timeout parameter * @return the arrival index of the current thread, where index * {@code getParties() - 1} indicates the first * to arrive and zero indicates the last to arrive * @throws InterruptedException if the current thread was interrupted * while waiting * @throws TimeoutException if the specified timeout elapses. * In this case the barrier will be broken. * @throws BrokenBarrierException if <em>another</em> thread was * interrupted or timed out while the current thread was * waiting, or the barrier was reset, or the barrier was broken * when {@code await} was called, or the barrier action (if * present) failed due to an exception */ public int await(long timeout, TimeUnit unit) throws InterruptedException, BrokenBarrierException, TimeoutException { return dowait(true, unit.toNanos(timeout)); }
dowait(boolean timed, long nanos): 可以说是最核心的方法
/** * 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) throw new BrokenBarrierException(); // 如果线程被打断, 执行breakBarrier()方法, 抛出异常 if (Thread.interrupted()) { breakBarrier(); throw new InterruptedException(); } // 参数为count -1 int index = --count; if (index == 0) { // tripped 参数为0, 触发障碍 // 运行行为标签: false boolean ranAction = false; try { // 阻碍命令 final Runnable command = barrierCommand; // 不为空就执行 if (command != null) command.run(); // 为空就把标签改为true ranAction = true; // 生成下一代 nextGeneration(); // 返回 0 return 0; } finally { // 如果标签不为true, 就打破障碍 if (!ranAction) breakBarrier(); } } // loop until tripped, broken, interrupted, or timed out // 自选直至障碍触发, 打破, 被打断 或超时 for (;;) { try { // 如果没有计时 if (!timed) trip.await(); // 如果纳秒数 > 0 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; // 如果有计时且纳秒数 <= 0, 就打破障碍, 抛出超时异常 if (timed && nanos <= 0L) { breakBarrier(); throw new TimeoutException(); } } } finally { lock.unlock(); } }
nextGeneration()
/** * Updates state on barrier trip and wakes up everyone. * 更新障碍条件的状态并唤醒所有线程 * Called only while holding lock. * 只有在上锁的状况下才能调用 */ private void nextGeneration() { // signal completion of last generation // 最后一代的信号完成 trip.signalAll(); // set up next generation // 设置下一代 count = parties; generation = new Generation(); }
breakBarrier()
/** * Sets current barrier generation as broken and wakes up everyone. * 设置当前障碍代数为破坏状态并唤醒所有线程 * Called only while holding lock. * 只有在上锁的状况下才能调用 */ private void breakBarrier() { generation.broken = true; count = parties; trip.signalAll(); }
getParties(): 获取线程组数
isBroken()
/** * Queries if this barrier is in a broken state. * 查看此障碍是否处于破坏状态 * * @return {@code true} if one or more parties broke out of this * barrier due to interruption or timeout since * construction or the last reset, or a barrier action * failed due to an exception; {@code false} otherwise. * 如果1个或者多个线程组因为打断, 超时, 重置 或一个障碍行为因异常而失败从而打破 * 了此障碍,就返回true */ public boolean isBroken() { final ReentrantLock lock = this.lock; lock.lock(); try { return generation.broken; } finally { lock.unlock(); } }
reset()
/** * Resets the barrier to its initial state. * 重设此障碍为它的初始状态 * If any parties are currently waiting at the barrier, they will return * with a {@link BrokenBarrierException}. * 如果任何线程组当前正等在此barrier, 它们会返回一个破坏障碍异常 * Note that resets <em>after</em> a breakage has occurred for other * reasons can be complicated to carry out; * 在代数破坏后重设障碍因为多重原因将很难执行 * threads need to re-synchronize in some other way, * and choose one to perform the reset. * 线程需要以其他方式重新同步, 并选择一种方式来执行重设 * It may be preferable to instead create a new barrier for subsequent * use. * 为了接下来的使用, reset方法可能重新创建一个新的障碍要好 */ public void reset() { final ReentrantLock lock = this.lock; lock.lock(); try { breakBarrier(); // break the current generation 破坏当前代数 nextGeneration(); // start a new generation 新创建一个代数 } finally { lock.unlock(); } }
getNumberWaiting()
/** * Returns the number of parties currently waiting at the barrier. * 返回当前线程组中正等待在障碍处的线程数 * This method is primarily useful for debugging and assertions. * 该方法主要用于debug 和 断言 * * @return the number of parties currently blocked in {@link #await} */ public int getNumberWaiting() { final ReentrantLock lock = this.lock; lock.lock(); try { return parties - count; } finally { lock.unlock(); } }
看完源码我们把之前的Demo再改一改
- 需要注意的是private修饰的dowait(), nextGeneration(), breakBarrier() 等方法不是我们能调用的, 而是都封装于对外的API中。
package com.ronnie;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo {
static class TaskThread extends Thread {
CyclicBarrier barrier;
TaskThread(CyclicBarrier barrier){
this.barrier = barrier;
}
@Override
public void run() {
try {
Thread.sleep(1000);
System.out.println(getName() + " attached barrier A");
System.out.println("Number of thread waiting at barrier A: " + barrier.getNumberWaiting());
System.out.println("Barrier A is broken? " + barrier.isBroken());
System.out.println("Party num: " + barrier.getParties());
barrier.await();
System.out.println(getName() + " breaks barrier A");
Thread.sleep(2000);
System.out.println(getName() + " attached barrier B");
System.out.println("Number of thread waiting at barrier B: " + barrier.getNumberWaiting());
System.out.println("Barrier B is broken? " + barrier.isBroken());
System.out.println("Party num: " + barrier.getParties());
barrier.await();
System.out.println(getName() + " breaks barrier B");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
int threadNum = 5;
CyclicBarrier barrier = new CyclicBarrier(threadNum, new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " finished the last task");
}
});
for (int i = 0; i < threadNum; i++){
new TaskThread(barrier).start();
}
}
}
执行结果
Thread-2 attached barrier A Thread-1 attached barrier A Number of thread waiting at barrier A: 0 Thread-0 attached barrier A Number of thread waiting at barrier A: 0 Thread-3 attached barrier A Number of thread waiting at barrier A: 0 Barrier A is broken? false Thread-4 attached barrier A Party num: 5 Barrier A is broken? false Party num: 5 Barrier A is broken? false Party num: 5 Number of thread waiting at barrier A: 0 Barrier A is broken? false Party num: 5 Number of thread waiting at barrier A: 0 Barrier A is broken? false Party num: 5 Thread-4 finished the last task Thread-4 breaks barrier A Thread-3 breaks barrier A Thread-0 breaks barrier A Thread-1 breaks barrier A Thread-2 breaks barrier A Thread-4 attached barrier B Number of thread waiting at barrier B: 0 Barrier B is broken? false Party num: 5 Thread-3 attached barrier B Number of thread waiting at barrier B: 1 Barrier B is broken? false Party num: 5 Thread-0 attached barrier B Number of thread waiting at barrier B: 2 Barrier B is broken? false Party num: 5 Thread-1 attached barrier B Number of thread waiting at barrier B: 3 Barrier B is broken? false Party num: 5 Thread-2 attached barrier B Number of thread waiting at barrier B: 4 Barrier B is broken? false Party num: 5 Thread-2 finished the last task Thread-2 breaks barrier B
使用场景
- 可用于多线程计算数据, 最后合并计算结果的场景。
CyclicBarrier 与 CountDownLatch区别
- CountDownLatch是一次性的, CylicBarrier 是可循环利用的
- CountDownLatch 参与的线程的职责是不一样的(倒计时, 等待倒计时结束), 而CyclicBarrier 参与的线程职责是一样的。
以上是关于CyclicBarrier 解读的主要内容,如果未能解决你的问题,请参考以下文章
Java Review - 并发编程_ 回环屏障CyclicBarrier原理&源码剖析