Java并发编程: CyclicBarrier让多线程齐步走

以前在<<编写高质量代码-改善Java程序的151个建议>>一书中看到有一节的标题是“CyclicBarrier让多线程齐步走”,觉得这标题挺不错的,所以在写这篇博文的时候也采用了这个名字。 

本文首先会介绍CyclicBarrier辅助工具类,其次将用CyclicBarrier工具类来完成一个实例,最后将给出CyclicBarrier和CountDownLatch的几点比较。 

之前关于CountDownLatch的博文,请参考如下链接: 
Java并发编程: 使用CountDownLatch协调子线程 - 
http://mouselearnjava.iteye.com/blog/1915438 

1. CyclicBarrier工具类介绍。 
CyclicBarrier是一个同步辅助工具类,它允许一组线程相互等待,直到到达一个公共的栏栅点。CyclicBarriers对于那些包含一组固定大小线程,并且这些线程必须不时地相互等待的程序非常有用。之所以将其称之为循环的Barrier是因为该Barrier在等待的线程释放之后可以重用。 
CyclicBarrier 支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后(但在释放所有线程之前),该命令只在每个屏障点运行一次。若在继续所有参与线程之前更新共享状态,此屏障操作 很有用。 

上面的介绍来自于CyclicBarrier类的注释。 

Java代码   收藏代码
  1. /** 
  2.  * A synchronization aid that allows a set of threads to all wait for 
  3.  * each other to reach a common barrier point.  CyclicBarriers are 
  4.  * useful in programs involving a fixed sized party of threads that 
  5.  * must occasionally wait for each other. The barrier is called 
  6.  * [i]cyclic[/i] because it can be re-used after the waiting threads 
  7.  * are released. 
  8.  * 
  9.  * 

    CyclicBarrier supports an optional {@link Runnable} command 

  10.  * that is run once per barrier point, after the last thread in the party 
  11.  * arrives, but before any threads are released. 
  12.  * This [i]barrier action[/i] is useful 
  13.  * for updating shared-state before any of the parties continue. 
  14.  */  




CyclicBarrier采用ConditionLock来完成线程之间的同步。相关的类图是CyclicBarrier类内容如下: 


Java并发编程: CyclicBarrier让多线程齐步走_第1张图片 

Java代码   收藏代码
  1. /* 
  2.  * @(#)CyclicBarrier.java   1.12 06/03/30 
  3.  * 
  4.  * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 
  5.  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 
  6.  */  
  7.   
  8. package java.util.concurrent;  
  9. import java.util.concurrent.locks.*;  
  10.   
  11. /** 
  12.  * A synchronization aid that allows a set of threads to all wait for 
  13.  * each other to reach a common barrier point.  CyclicBarriers are 
  14.  * useful in programs involving a fixed sized party of threads that 
  15.  * must occasionally wait for each other. The barrier is called 
  16.  * [i]cyclic[/i] because it can be re-used after the waiting threads 
  17.  * are released. 
  18.  * 
  19.  * 

    CyclicBarrier supports an optional {@link Runnable} command 

  20.  * that is run once per barrier point, after the last thread in the party 
  21.  * arrives, but before any threads are released. 
  22.  * This [i]barrier action[/i] is useful 
  23.  * for updating shared-state before any of the parties continue. 
  24.  * 
  25.  * 

    Sample usage: Here is an example of 

  26.  *  using a barrier in a parallel decomposition design: 
  27.  * 
     
  28.  * class Solver { 
  29.  *   final int N; 
  30.  *   final float[][] data; 
  31.  *   final CyclicBarrier barrier; 
  32.  * 
  33.  *   class Worker implements Runnable { 
  34.  *     int myRow; 
  35.  *     Worker(int row) { myRow = row; } 
  36.  *     public void run() { 
  37.  *       while (!done()) { 
  38.  *         processRow(myRow); 
  39.  * 
  40.  *         try { 
  41.  *           barrier.await(); 
  42.  *         } catch (InterruptedException ex) { 
  43.  *           return; 
  44.  *         } catch (BrokenBarrierException ex) { 
  45.  *           return; 
  46.  *         } 
  47.  *       } 
  48.  *     } 
  49.  *   } 
  50.  * 
  51.  *   public Solver(float[][] matrix) { 
  52.  *     data = matrix; 
  53.  *     N = matrix.length; 
  54.  *     barrier = new CyclicBarrier(N, 
  55.  *                                 new Runnable() { 
  56.  *                                   public void run() { 
  57.  *                                     mergeRows(...); 
  58.  *                                   } 
  59.  *                                 }); 
  60.  *     for (int i = 0; i < N; ++i) 
  61.  *       new Thread(new Worker(i)).start(); 
  62.  * 
  63.  *     waitUntilDone(); 
  64.  *   } 
  65.  * } 
  66.  *  
  67.  * Here, each worker thread processes a row of the matrix then waits at the 
  68.  * barrier until all rows have been processed. When all rows are processed 
  69.  * the supplied {@link Runnable} barrier action is executed and merges the 
  70.  * rows. If the merger 
  71.  * determines that a solution has been found then done() will return 
  72.  * true and each worker will terminate. 
  73.  * 
  74.  * 

    If the barrier action does not rely on the parties being suspended when 

  75.  * it is executed, then any of the threads in the party could execute that 
  76.  * action when it is released. To facilitate this, each invocation of 
  77.  * {@link #await} returns the arrival index of that thread at the barrier. 
  78.  * You can then choose which thread should execute the barrier action, for 
  79.  * example: 
  80.  * 
      if (barrier.await() == 0) { 
  81.  *     // log the completion of this iteration 
  82.  *   } 
  83.  * 
  84.  * 

    The CyclicBarrier uses an all-or-none breakage model 

  85.  * for failed synchronization attempts: If a thread leaves a barrier 
  86.  * point prematurely because of interruption, failure, or timeout, all 
  87.  * other threads waiting at that barrier point will also leave 
  88.  * abnormally via {@link BrokenBarrierException} (or 
  89.  * {@link InterruptedException} if they too were interrupted at about 
  90.  * the same time). 
  91.  * 
  92.  * 

    Memory consistency effects: Actions in a thread prior to calling 

  93.  * {@code await()} 
  94.  * [url=package-summary.html#MemoryVisibility]happen-before[/url] 
  95.  * actions that are part of the barrier action, which in turn 
  96.  * happen-before actions following a successful return from the 
  97.  * corresponding {@code await()} in other threads. 
  98.  * 
  99.  * @since 1.5 
  100.  * @see CountDownLatch 
  101.  * 
  102.  * @author Doug Lea 
  103.  */  
  104. public class CyclicBarrier {  
  105.     /** 
  106.      * Each use of the barrier is represented as a generation instance. 
  107.      * The generation changes whenever the barrier is tripped, or 
  108.      * is reset. There can be many generations associated with threads 
  109.      * using the barrier - due to the non-deterministic way the lock 
  110.      * may be allocated to waiting threads - but only one of these 
  111.      * can be active at a time (the one to which count applies) 
  112.      * and all the rest are either broken or tripped. 
  113.      * There need not be an active generation if there has been a break 
  114.      * but no subsequent reset. 
  115.      */  
  116.     private static class Generation {  
  117.         boolean broken = false;  
  118.     }  
  119.   
  120.     /** The lock for guarding barrier entry */  
  121.     private final ReentrantLock lock = new ReentrantLock();  
  122.     /** Condition to wait on until tripped */  
  123.     private final Condition trip = lock.newCondition();  
  124.     /** The number of parties */  
  125.     private final int parties;  
  126.     /* The command to run when tripped */  
  127.     private final Runnable barrierCommand;  
  128.     /** The current generation */  
  129.     private Generation generation = new Generation();  
  130.   
  131.     /** 
  132.      * Number of parties still waiting. Counts down from parties to 0 
  133.      * on each generation.  It is reset to parties on each new 
  134.      * generation or when broken. 
  135.      */  
  136.     private int count;  
  137.   
  138.     /** 
  139.      * Updates state on barrier trip and wakes up everyone. 
  140.      * Called only while holding lock. 
  141.      */  
  142.     private void nextGeneration() {  
  143.         // signal completion of last generation  
  144.         trip.signalAll();  
  145.         // set up next generation  
  146.         count = parties;  
  147.         generation = new Generation();  
  148.     }  
  149.   
  150.     /** 
  151.      * Sets current barrier generation as broken and wakes up everyone. 
  152.      * Called only while holding lock. 
  153.      */  
  154.     private void breakBarrier() {  
  155.         generation.broken = true;  
  156.     count = parties;  
  157.         trip.signalAll();  
  158.     }  
  159.   
  160.     /** 
  161.      * Main barrier code, covering the various policies. 
  162.      */  
  163.     private int dowait(boolean timed, long nanos)  
  164.         throws InterruptedException, BrokenBarrierException,  
  165.                TimeoutException {  
  166.         final ReentrantLock lock = this.lock;  
  167.         lock.lock();  
  168.         try {  
  169.             final Generation g = generation;  
  170.   
  171.             if (g.broken)  
  172.                 throw new BrokenBarrierException();  
  173.   
  174.             if (Thread.interrupted()) {  
  175.                 breakBarrier();  
  176.                 throw new InterruptedException();  
  177.             }  
  178.   
  179.            int index = --count;  
  180.            if (index == 0) {  // tripped  
  181.                boolean ranAction = false;  
  182.                try {  
  183.            final Runnable command = barrierCommand;  
  184.                    if (command != null)  
  185.                        command.run();  
  186.                    ranAction = true;  
  187.                    nextGeneration();  
  188.                    return 0;  
  189.                } finally {  
  190.                    if (!ranAction)  
  191.                        breakBarrier();  
  192.                }  
  193.            }  
  194.   
  195.             // loop until tripped, broken, interrupted, or timed out  
  196.             for (;;) {  
  197.                 try {  
  198.                     if (!timed)  
  199.                         trip.await();  
  200.                     else if (nanos > 0L)  
  201.                         nanos = trip.awaitNanos(nanos);  
  202.                 } catch (InterruptedException ie) {  
  203.                     if (g == generation && ! g.broken) {  
  204.                         breakBarrier();  
  205.             throw ie;  
  206.             } else {  
  207.             // We're about to finish waiting even if we had not  
  208.             // been interrupted, so this interrupt is deemed to  
  209.             // "belong" to subsequent execution.  
  210.             Thread.currentThread().interrupt();  
  211.             }  
  212.                 }  
  213.   
  214.                 if (g.broken)  
  215.                     throw new BrokenBarrierException();  
  216.   
  217.                 if (g != generation)  
  218.                     return index;  
  219.   
  220.                 if (timed && nanos <= 0L) {  
  221.                     breakBarrier();  
  222.                     throw new TimeoutException();  
  223.                 }  
  224.             }  
  225.         } finally {  
  226.             lock.unlock();  
  227.         }  
  228.     }  
  229.   
  230.     /** 
  231.      * Creates a new CyclicBarrier that will trip when the 
  232.      * given number of parties (threads) are waiting upon it, and which 
  233.      * will execute the given barrier action when the barrier is tripped, 
  234.      * performed by the last thread entering the barrier. 
  235.      * 
  236.      * @param parties the number of threads that must invoke {@link #await} 
  237.      *        before the barrier is tripped 
  238.      * @param barrierAction the command to execute when the barrier is 
  239.      *        tripped, or {@code null} if there is no action 
  240.      * @throws IllegalArgumentException if {@code parties} is less than 1 
  241.      */  
  242.     public CyclicBarrier(int parties, Runnable barrierAction) {  
  243.         if (parties <= 0throw new IllegalArgumentException();  
  244.         this.parties = parties;  
  245.         this.count = parties;  
  246.         this.barrierCommand = barrierAction;  
  247.     }  
  248.   
  249.     /** 
  250.      * Creates a new CyclicBarrier that will trip when the 
  251.      * given number of parties (threads) are waiting upon it, and 
  252.      * does not perform a predefined action when the barrier is tripped. 
  253.      * 
  254.      * @param parties the number of threads that must invoke {@link #await} 
  255.      *        before the barrier is tripped 
  256.      * @throws IllegalArgumentException if {@code parties} is less than 1 
  257.      */  
  258.     public CyclicBarrier(int parties) {  
  259.         this(parties, null);  
  260.     }  
  261.   
  262.     /** 
  263.      * Returns the number of parties required to trip this barrier. 
  264.      * 
  265.      * @return the number of parties required to trip this barrier 
  266.      */  
  267.     public int getParties() {  
  268.         return parties;  
  269.     }  
  270.   
  271.     /** 
  272.      * Waits until all {@linkplain #getParties parties} have invoked 
  273.      * await on this barrier. 
  274.      * 
  275.      * 

    If the current thread is not the last to arrive then it is 

  276.      * disabled for thread scheduling purposes and lies dormant until 
  277.      * one of the following things happens: 
  278.      * [list] 
  279.      * 
  280. The last thread arrives; or 
  281.      * 
  282. Some other thread {@linkplain Thread#interrupt interrupts} 
  283.      * the current thread; or 
  284.      * 
  285. Some other thread {@linkplain Thread#interrupt interrupts} 
  286.      * one of the other waiting threads; or 
  287.      * 
  288. Some other thread times out while waiting for barrier; or 
  289.      * 
  290. Some other thread invokes {@link #reset} on this barrier. 
  291.      * [/list] 
  292.      * 
  293.      * 

    If the current thread: 

  294.      * [list] 
  295.      * 
  296. has its interrupted status set on entry to this method; or 
  297.      * 
  298. is {@linkplain Thread#interrupt interrupted} while waiting 
  299.      * [/list] 
  300.      * then {@link InterruptedException} is thrown and the current thread's 
  301.      * interrupted status is cleared. 
  302.      * 
  303.      * 

    If the barrier is {@link #reset} while any thread is waiting, 

  304.      * or if the barrier {@linkplain #isBroken is broken} when 
  305.      * await is invoked, or while any thread is waiting, then 
  306.      * {@link BrokenBarrierException} is thrown. 
  307.      * 
  308.      * 

    If any thread is {@linkplain Thread#interrupt interrupted} while waiting, 

  309.      * then all other waiting threads will throw 
  310.      * {@link BrokenBarrierException} and the barrier is placed in the broken 
  311.      * state. 
  312.      * 
  313.      * 

    If the current thread is the last thread to arrive, and a 

  314.      * non-null barrier action was supplied in the constructor, then the 
  315.      * current thread runs the action before allowing the other threads to 
  316.      * continue. 
  317.      * If an exception occurs during the barrier action then that exception 
  318.      * will be propagated in the current thread and the barrier is placed in 
  319.      * the broken state. 
  320.      * 
  321.      * @return the arrival index of the current thread, where index 
  322.      *         {@link #getParties()} - 1 indicates the first 
  323.      *         to arrive and zero indicates the last to arrive 
  324.      * @throws InterruptedException if the current thread was interrupted 
  325.      *         while waiting 
  326.      * @throws BrokenBarrierException if [i]another[/i] thread was 
  327.      *         interrupted or timed out while the current thread was 
  328.      *         waiting, or the barrier was reset, or the barrier was 
  329.      *         broken when {@code await} was called, or the barrier 
  330.      *         action (if present) failed due an exception. 
  331.      */  
  332.     public int await() throws InterruptedException, BrokenBarrierException {  
  333.         try {  
  334.             return dowait(false, 0L);  
  335.         } catch (TimeoutException toe) {  
  336.             throw new Error(toe); // cannot happen;  
  337.         }  
  338.     }  
  339.   
  340.     /** 
  341.      * Waits until all {@linkplain #getParties parties} have invoked 
  342.      * await on this barrier, or the specified waiting time elapses. 
  343.      * 
  344.      * 

    If the current thread is not the last to arrive then it is 

  345.      * disabled for thread scheduling purposes and lies dormant until 
  346.      * one of the following things happens: 
  347.      * [list] 
  348.      * 
  349. The last thread arrives; or 
  350.      * 
  351. The specified timeout elapses; or 
  352.      * 
  353. Some other thread {@linkplain Thread#interrupt interrupts} 
  354.      * the current thread; or 
  355.      * 
  356. Some other thread {@linkplain Thread#interrupt interrupts} 
  357.      * one of the other waiting threads; or 
  358.      * 
  359. Some other thread times out while waiting for barrier; or 
  360.      * 
  361. Some other thread invokes {@link #reset} on this barrier. 
  362.      * [/list] 
  363.      * 
  364.      * 

    If the current thread: 

  365.      * [list] 
  366.      * 
  367. has its interrupted status set on entry to this method; or 
  368.      * 
  369. is {@linkplain Thread#interrupt interrupted} while waiting 
  370.      * [/list] 
  371.      * then {@link InterruptedException} is thrown and the current thread's 
  372.      * interrupted status is cleared. 
  373.      * 
  374.      * 

    If the specified waiting time elapses then {@link TimeoutException} 

  375.      * is thrown. If the time is less than or equal to zero, the 
  376.      * method will not wait at all. 
  377.      * 
  378.      * 

    If the barrier is {@link #reset} while any thread is waiting, 

  379.      * or if the barrier {@linkplain #isBroken is broken} when 
  380.      * await is invoked, or while any thread is waiting, then 
  381.      * {@link BrokenBarrierException} is thrown. 
  382.      * 
  383.      * 

    If any thread is {@linkplain Thread#interrupt interrupted} while 

  384.      * waiting, then all other waiting threads will throw {@link 
  385.      * BrokenBarrierException} and the barrier is placed in the broken 
  386.      * state. 
  387.      * 
  388.      * 

    If the current thread is the last thread to arrive, and a 

  389.      * non-null barrier action was supplied in the constructor, then the 
  390.      * current thread runs the action before allowing the other threads to 
  391.      * continue. 
  392.      * If an exception occurs during the barrier action then that exception 
  393.      * will be propagated in the current thread and the barrier is placed in 
  394.      * the broken state. 
  395.      * 
  396.      * @param timeout the time to wait for the barrier 
  397.      * @param unit the time unit of the timeout parameter 
  398.      * @return the arrival index of the current thread, where index 
  399.      *         {@link #getParties()} - 1 indicates the first 
  400.      *         to arrive and zero indicates the last to arrive 
  401.      * @throws InterruptedException if the current thread was interrupted 
  402.      *         while waiting 
  403.      * @throws TimeoutException if the specified timeout elapses 
  404.      * @throws BrokenBarrierException if [i]another[/i] thread was 
  405.      *         interrupted or timed out while the current thread was 
  406.      *         waiting, or the barrier was reset, or the barrier was broken 
  407.      *         when {@code await} was called, or the barrier action (if 
  408.      *         present) failed due an exception 
  409.      */  
  410.     public int await(long timeout, TimeUnit unit)  
  411.         throws InterruptedException,  
  412.                BrokenBarrierException,  
  413.                TimeoutException {  
  414.         return dowait(true, unit.toNanos(timeout));  
  415.     }  
  416.   
  417.     /** 
  418.      * Queries if this barrier is in a broken state. 
  419.      * 
  420.      * @return {@code true} if one or more parties broke out of this 
  421.      *         barrier due to interruption or timeout since 
  422.      *         construction or the last reset, or a barrier action 
  423.      *         failed due to an exception; {@code false} otherwise. 
  424.      */  
  425.     public boolean isBroken() {  
  426.         final ReentrantLock lock = this.lock;  
  427.         lock.lock();  
  428.         try {  
  429.             return generation.broken;  
  430.         } finally {  
  431.             lock.unlock();  
  432.         }  
  433.     }  
  434.   
  435.     /** 
  436.      * Resets the barrier to its initial state.  If any parties are 
  437.      * currently waiting at the barrier, they will return with a 
  438.      * {@link BrokenBarrierException}. Note that resets [i]after[/i] 
  439.      * a breakage has occurred for other reasons can be complicated to 
  440.      * carry out; threads need to re-synchronize in some other way, 
  441.      * and choose one to perform the reset.  It may be preferable to 
  442.      * instead create a new barrier for subsequent use. 
  443.      */  
  444.     public void reset() {  
  445.         final ReentrantLock lock = this.lock;  
  446.         lock.lock();  
  447.         try {  
  448.             breakBarrier();   // break the current generation  
  449.             nextGeneration(); // start a new generation  
  450.         } finally {  
  451.             lock.unlock();  
  452.         }  
  453.     }  
  454.   
  455.     /** 
  456.      * Returns the number of parties currently waiting at the barrier. 
  457.      * This method is primarily useful for debugging and assertions. 
  458.      * 
  459.      * @return the number of parties currently blocked in {@link #await} 
  460.      */  
  461.     public int getNumberWaiting() {  
  462.         final ReentrantLock lock = this.lock;  
  463.         lock.lock();  
  464.         try {  
  465.             return parties - count;  
  466.         } finally {  
  467.             lock.unlock();  
  468.         }  
  469.     }  
  470. }  



2. CyclicBarrier工具类的使用案例 

CyclicBarrier可以让所有线程都处于等待状态(阻塞),然后在满足条件的情况下继续执行。打个比方: 几个小组包一辆车去旅游,一天行程包括上午小组自由活动和下午自由活动:各个小组早上自由活动,但是11点半大巴车上集合,然后吃饭并赶赴下一个景区。 
各个小组下午自由活动,但是要5点半大巴车上集合,然后一起回去。 

Java代码   收藏代码
  1. package my.concurrent.cyclicbarrier;  
  2.   
  3. import java.util.concurrent.BrokenBarrierException;  
  4. import java.util.concurrent.CyclicBarrier;  
  5.   
  6. public class TeamGroup implements Runnable {  
  7.   
  8.     private final CyclicBarrier barrier;  
  9.   
  10.     private int groupNumber;  
  11.   
  12.     /** 
  13.      * @param barrier 
  14.      * @param groupNumber 
  15.      */  
  16.     public TeamGroup(CyclicBarrier barrier, int groupNumber) {  
  17.         this.barrier = barrier;  
  18.         this.groupNumber = groupNumber;  
  19.     }  
  20.   
  21.     public void run() {  
  22.   
  23.         try {  
  24.             print();  
  25.             barrier.await();  
  26.         } catch (InterruptedException e) {  
  27.             // TODO Auto-generated catch block  
  28.             e.printStackTrace();  
  29.         } catch (BrokenBarrierException e) {  
  30.             // TODO Auto-generated catch block  
  31.             e.printStackTrace();  
  32.         }  
  33.     }  
  34.   
  35.     private void print() {  
  36.         System.out.println(String.format("第%d组完成该地景点浏览,并回到集合点", groupNumber));  
  37.     }  
  38.   
  39. }  



Java代码   收藏代码
  1. package my.concurrent.cyclicbarrier;  
  2.   
  3. import java.util.concurrent.CyclicBarrier;  
  4. import java.util.concurrent.ExecutorService;  
  5. import java.util.concurrent.Executors;  
  6.   
  7. public class CyclicBarrierTest {  
  8.   
  9.     private static final int THREAD_SLEEP_MILLIS = 6000;  
  10.   
  11.     /** 旅游小数的个数 */  
  12.     private static final int NUMBER_OF_GROUPS = 6;  
  13.   
  14.     /** 观光是否结束的标识 */  
  15.     private static boolean tourOver = false;  
  16.   
  17.     public static void main(String[] args) {  
  18.   
  19.         ExecutorService service = Executors  
  20.                 .newFixedThreadPool(NUMBER_OF_GROUPS);  
  21.   
  22.         CyclicBarrier cb = new CyclicBarrier(NUMBER_OF_GROUPS, new Runnable() {  
  23.   
  24.             public void run() {  
  25.                 /* 
  26.                  * 如果一天的游玩结束了,大家可以坐大巴回去了... ... 
  27.                  */  
  28.                 if (isTourOver()) {  
  29.                     System.out.println("各个小组都集合到大巴上,准备回家.. ...");  
  30.                 }  
  31.   
  32.             }  
  33.         });  
  34.   
  35.         System.out.println("用CyclicBarrier辅助工具类模拟旅游过程中小组集合::");  
  36.   
  37.         /* 
  38.          * 上午各个小组自由活动,然后在某个点,比如11点半集合到大巴上。 
  39.          */  
  40.         tourInTheMorning(service, cb);  
  41.         sleep(THREAD_SLEEP_MILLIS);  
  42.   
  43.         /* 
  44.          * 调用reset方法,将barrier设置到初始化状态。 
  45.          *  
  46.          * TODO://不知道这样的调用是否是合理的? 
  47.          */  
  48.         cb.reset();  
  49.   
  50.         /* 
  51.          * 下午各个小组自由活动,然后在某个点,比如11点半集合到大巴上。 
  52.          */  
  53.         tourInTheAfternoon(service, cb);  
  54.   
  55.         /* 
  56.          * 下午小组集合完毕后,一天的观光就结束了,将标志位记为true; 
  57.          */  
  58.         tourOver = true;  
  59.   
  60.         sleep(THREAD_SLEEP_MILLIS);  
  61.         service.shutdown();  
  62.   
  63.     }  
  64.   
  65.     /** 
  66.      * @return the tourOver 
  67.      */  
  68.     public static boolean isTourOver() {  
  69.         return tourOver;  
  70.     }  
  71.   
  72.     /** 
  73.      * @param tourOver 
  74.      *            the tourOver to set 
  75.      */  
  76.     public static void setTourOver(boolean tourOver) {  
  77.         CyclicBarrierTest.tourOver = tourOver;  
  78.     }  
  79.   
  80.     private static void tourInTheMorning(ExecutorService service,  
  81.             final CyclicBarrier cb) {  
  82.         System.out.println("早上自由玩... ... ");  
  83.         for (int groupNumber = 1; groupNumber <= NUMBER_OF_GROUPS; groupNumber++) {  
  84.             service.execute(new TeamGroup(cb, groupNumber));  
  85.         }  
  86.     }  
  87.   
  88.     private static void tourInTheAfternoon(ExecutorService service,  
  89.             final CyclicBarrier cb) {  
  90.         System.out.println("下午自由玩... ... ");  
  91.         for (int groupNumber = 1; groupNumber <= NUMBER_OF_GROUPS; groupNumber++) {  
  92.             service.execute(new TeamGroup(cb, groupNumber));  
  93.         }  
  94.     }  
  95.   
  96.     private static void sleep(long millis) {  
  97.         try {  
  98.             Thread.sleep(millis);  
  99.         } catch (InterruptedException e) {  
  100.             // TODO Auto-generated catch block  
  101.             e.printStackTrace();  
  102.         }  
  103.     }  
  104. }  



某一次运行的结果如下: 

用CyclicBarrier辅助工具类模拟旅游过程中小组集合:: 
早上自由玩... ... 
第1组完成该地景点浏览,并回到集合点 
第6组完成该地景点浏览,并回到集合点 
第2组完成该地景点浏览,并回到集合点 
第5组完成该地景点浏览,并回到集合点 
第4组完成该地景点浏览,并回到集合点 
第3组完成该地景点浏览,并回到集合点 
下午自由玩... ... 
第1组完成该地景点浏览,并回到集合点 
第3组完成该地景点浏览,并回到集合点 
第4组完成该地景点浏览,并回到集合点 
第5组完成该地景点浏览,并回到集合点 
第6组完成该地景点浏览,并回到集合点 
第2组完成该地景点浏览,并回到集合点 
各个小组都集合到大巴上,准备回家.. ... 

3. CyclicBarrier vs. CountDownLatch 

相同点:   
    两者都是用于线程同步的辅助工具类,都提供了await方法来达到线程等待。 

不同点: 
1. 从类的实现上看: 
    CountDownLatch通过一个继承AbstractQueuedSynchronizer的内部类Sync来完成同步。 
    CyclicBarrier通过Condition和Lock来完成同步。 
2. 从类的用途上看: 
    CountDownLatch: 一个或者是一部分线程,等待另外一部线程都完成操作。 
    CyclicBarrier: 所有线程互相等待完成。 
3. 从适合场合来看: 
CountDownLatch中计数是不能被重置的。如果需要一个可以重置计数的版本,需要考虑使用CyclicBarrie。 
CountDownLatch适用于一次同步。当使用CountDownLatch时,任何线程允许多次调用countDown(). 那些调用了await()方法的线程将被阻塞,直到那些没有被阻塞线程调用countDown()使计数到达0为止。 

Java并发编程: CyclicBarrier让多线程齐步走_第2张图片 

相反,CyclicBarrier适用于多个同步点。例如:一组正在运算的线程,在进入下一个阶段计算之前需要同步。 

 

与CountDownLatch不同,一个处于某个阶段的线程调用了await()方法将会被阻塞,直到所有属于这个阶段的线程都调用了await()方法为止。 

在CyclicBarrier中,如果一个线程由于中断,失败或者超时等原因,过早地离开了栅栏点,那么所有在栅栏点等待的其它线程也会通过BrokenBarrierException或者IterupedException异常地离开。 

 

4. 从关注点上来看: 使用CountDownLatch时,它关注的一个线程或者多个线程需要在其它在一组线程完成操作之后,在去做一些事情。比如:服务的启动等。CyclicBarrier更加关注的是公共的栅栏点(Common Barrier point),关注的是这个点上的同步。这个点之前之后的事情并不需要太多的关注。比如:一个并行计算需要分几个阶段完成,在一个阶段完成进入到下一个阶段之前,需要同步,这时候CyclicBarrie很适合。 

由于知识的原因,上述例子以及CountDownLatch和CyclicBarrier的比较上会存在不足,如果有问题请大家指正,也希望大家能够提供两者其它方面的不同之处,一起学习分享。

你可能感兴趣的:(java线程)