package cn.com.pep; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; /** * 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. 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. * 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: * * <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. * * <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. * 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: 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()} * <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. * * @since 1.5 * @see CountDownLatch2 * * @author Doug Lea */ public class CyclicBarrier { /** * Each use of the barrier is represented as a generation instance. * 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. * There need not be an active generation if there has been a break * but no subsequent reset. */ /** * @Description:CyclicBarrier的类似于CountDownLatch,实现了一组线程之间的相互等待,直到所有线程都到齐了,再向下执行。 * 而与CountDownLatch不同的是,其实现采用的是条件队列,而非共享锁,并且是可以重用的。 * 那如何理解Generation这个“代”呢? * 打个比方:游乐场里的过山车有10个座位,每次都凑满了10个之后,管理员才打开门,让10个游客过去,开动过山车。然后再关门,其余的游客再继续等待,等再凑够“下一波”的10个人, * 这个模型中,前面已经通过的10个游客,就是“上一代”,而这个大门就是“barrier”,可以重复开启,后面继续等待排队的下一波游客,就是下一代。 * @version: V1.0 * @author: wwh * @date: 2023年4月23日 上午9:52:52 */ 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 */ //需要一起通过barrier的线程数 private final int parties; /* The command to run when tripped */ //当所有线程通过barrier之前,需要做的工作 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. */ //此barrier需要等待的线程数,初始值为parties,在同一个generation中,每调用一次await(),自减1 private int count; /** * Updates state on barrier trip and wakes up everyone. * Called only while holding lock. */ //开启下一代 private void nextGeneration() { // signal completion of last generation //唤醒条件队列trip中所有等待的线程 trip.signalAll(); // set up next generation //初始化count count = parties; //开启新一代,即将“代”的打破标记broken设置为false generation = new Generation(); } /** * Sets current barrier generation as broken and wakes up everyone. * Called only while holding lock. */ //打破当前barrier,这个怎么理解呢?某一时段景区的人比较少,很久都凑不够10个人,这时候管理员没办法,就打开门(barrier),将这一拨人(generation)放进去,乘坐过山车,此时,管理员的这个行为就是一个breakBarrier。 private void breakBarrier() { //将当前generation打破标记broken设置为true generation.broken = true; //重置count count = parties; //唤醒条件队列condition中正在等待的线程 trip.signalAll(); } /** * 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; /** * 只有调用breakBarrier(),才能将当前generation的broken标记置为true,如果当前线程在执行dowait()时候,发现当前的generation已经被标记为true, * 则直接抛出BrokenBarrierException异常。 */ if (g.broken) throw new BrokenBarrierException(); /** * 此时,假如检测到当前线程被“中断”了,那么就应该将当前barrier打破,这是因为处于同一个barrier中的所有线程是相互等待的。既然当前线程已经被中断了, * 那么其它线程肯定等不到当前线程执行完成了,所以执行breakBarrier()方法,将当前barrier打破,唤醒其他线程,其他线程被唤醒后,发现当前的generation的 * broken标记已经为true,则这些被唤醒的线程也都抛出BrokenBarrierExceptoin异常。 * CyclicBarrier实现中采用了“all-or-none breakage model”模型,即全损或者全无损模型。 */ if (Thread.interrupted()) { //检测到当前线程中断,打破当前线程所处的barrier,并且抛出InterruptedException异常。 breakBarrier(); throw new InterruptedException(); } //用来表示当前线程进入到barrier的顺序,index=parties-1,表示第一个进入到barrier的线程,index=parties-2表示第二个,依次类推,知道index =0表示最后一个进入barrier的线程 int index = --count; //index == 0,所有线程都已经就绪了,准备通过barrier,这个条件只有barrier中的最后一个进来线程才能达到。 if (index == 0) { // tripped boolean ranAction = false; try { //所有的线程通过barrier之前,执行完通过barrier之前的准备工作(由barrier中的最后一个线程来执行),然后所有的线程再通过barrier。 final Runnable command = barrierCommand; if (command != null) command.run(); ranAction = true; //唤醒当前代中处于等待状态的线程,开启新的一代 nextGeneration(); return 0; } finally { //说明最后一个进入barrier的线程在执行通过barrier的准备时候发生了异常,则打破当前barrier if (!ranAction) breakBarrier(); } } // loop until tripped, broken, interrupted, or timed out //如果index数不为0,就表示还有需要等待的其他线程,所以将当前线程挂起,等待所有线程都到齐、或者超时、被其他线程中断 for (;;) { try { //阻塞等待,等待被其他线程中断唤醒或者sigal()唤醒 if (!timed) trip.await(); else if (nanos > 0L) //带超时时间的阻塞,等到超时时间到 nanos = trip.awaitNanos(nanos); } catch (InterruptedException ie) { //等待的过程中发生了中断异常 if (g == generation && ! g.broken) { //当前线程还处于这个“代”中,并且这个barrier还没有被打破,那么需要将这个barrier打破,结束这个barrier中其他线程的无异议的等待 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. /** * 走到这里来了,应该有两种情况: * 1、说明新的代已经产生了,当前线程已经不在这个代中了,那么只是重置先线程的中断标记,不需要做额外处理了; * 2、说明中断发生时候,barrier已经被打破了,所以也只是记录下线程的中断标记; * 3、 */ Thread.currentThread().interrupt(); } } /** * 能走到这里来说明,线程已经被唤醒了,检查一下broken的状态,如果为true则直接抛出BrokenBarrierException异常, * 能使broken的状态变为true的只有breakBarrier()方法,对应以下几种情况: * 1、其他在执行await()方法线程被挂起之前,就已经被中断了,导致触发了breakBarrier()方法; * 2、其他在执行await()方法线程在被挂起等待的过程中,被中断唤醒了,导致触发了breakBarrier()方法; * 3、最后一个进入barrier的线程在执行barrierCommand过程中发生了异常,导致breakBarrier()方法被调用; * 4、显示调用reset()方法; */ if (g.broken) throw new BrokenBarrierException(); //如果线程被正常唤醒,下一代已经被开启了,则返回线程进入barrier的顺序 if (g != generation) return index; //允许超时,并且超时时间已到,则打破当前barrier,并抛出TimeOutException异常; if (timed && nanos <= 0L) { breakBarrier(); throw new TimeoutException(); } } } finally { lock.unlock(); } } /** * 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); } /** * Returns the number of parties required to trip this barrier. * * @return the number of parties required to trip this barrier */ //需要通过这个barrier的线程数 public int getParties() { return parties; } /** * Waits until all {@linkplain #getParties parties} have invoked * {@code await} on this barrier. * * <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. * </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 * @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 } } /** * 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)); } /** * 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. */ public boolean isBroken() { final ReentrantLock lock = this.lock; lock.lock(); try { return generation.broken; } finally { lock.unlock(); } } /** * Resets the barrier to its initial state. If any parties are * currently waiting at the barrier, they will return with a * {@link BrokenBarrierException}. 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. */ public void reset() { final ReentrantLock lock = this.lock; lock.lock(); try { breakBarrier(); // break the current generation nextGeneration(); // start a new generation } finally { lock.unlock(); } } /** * Returns the number of parties currently waiting at the barrier. * This method is primarily useful for debugging and assertions. * * @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(); } } }
本文来自博客园,作者:一只烤鸭朝北走,仅用于技术学习,所有资源都来源于网络,部分是转发,部分是个人总结。欢迎共同学习和转载,转载请在醒目位置标明原文。如有侵权,请留言告知,及时撤除。转载请注明原文链接:https://www.cnblogs.com/wha6239/p/17346295.html