cyclicbarrier java_Java多线程系列--“JUC锁”10之 CyclicBarrier原理和示例

1 /*

2 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.3 *4 *5 *6 *7 *8 *9 *10 *11 *12 *13 *14 *15 *16 *17 *18 *19 *20 *21 *22 *23 */

24

25 /*

26 *27 *28 *29 *30 *31 * Written by Doug Lea with assistance from members of JCP JSR-16632 * Expert Group and released to the public domain, as explained at33 *http://creativecommons.org/publicdomain/zero/1.0/

34 */

35

36 packagejava.util.concurrent;37 import java.util.concurrent.locks.*;38

39 /**

40 * A synchronization aid that allows a set of threads to all wait for41 * each other to reach a common barrier point. CyclicBarriers are42 * useful in programs involving a fixed sized party of threads that43 * must occasionally wait for each other. The barrier is called44 * cyclic because it can be re-used after the waiting threads45 * are released.46 *47 *

A CyclicBarrier supports an optional {@linkRunnable} command48 * that is run once per barrier point, after the last thread in the party49 * arrives, but before any threads are released.50 * This barrier action is useful51 * for updating shared-state before any of the parties continue.52 *53 *

Sample usage: Here is an example of54 * using a barrier in a parallel decomposition design:55 *

56 * class Solver {57 *   final int N;58 *   final float[][] data;59 *   final CyclicBarrier barrier;60 *61 *   class Worker implements Runnable {62 *     int myRow;63 *     Worker(int row) { myRow = row; }64 *     public void run() {65 *       while (!done()) {66 *         processRow(myRow);67 *68 *         try {69 *           barrier.await();70 *         } catch (InterruptedException ex) {71 *           return;72 *         } catch (BrokenBarrierException ex) {73 *           return;74 *         }75 *       }76 *     }77 *   }78 *79 *   public Solver(float[][] matrix) {80 *     data = matrix;81 *     N = matrix.length;82 *     barrier = new CyclicBarrier(N,83 *                                 new Runnable() {84 *                                   public void run() {85 *                                     mergeRows(...);86 *                                   }87 *                                 });88 *     for (int i = 0; i < N; ++i)89 *       new Thread(new Worker(i)).start();90 *91 *     waitUntilDone();92 *   }93 * }94 * 
95 * Here, each worker thread processes a row of the matrix then waits at the96 * barrier until all rows have been processed. When all rows are processed97 * the supplied {@linkRunnable} barrier action is executed and merges the98 * rows. If the merger99 * determines that a solution has been found then done() will return100 * true and each worker will terminate.101 *102 *

If the barrier action does not rely on the parties being suspended when103 * it is executed, then any of the threads in the party could execute that104 * action when it is released. To facilitate this, each invocation of105 * {@link#await} returns the arrival index of that thread at the barrier.106 * You can then choose which thread should execute the barrier action, for107 * example:108 *

  if (barrier.await() == 0) {109 *     // log the completion of this iteration110 *   }
111 *112 *

The CyclicBarrier uses an all-or-none breakage model113 * for failed synchronization attempts: If a thread leaves a barrier114 * point prematurely because of interruption, failure, or timeout, all115 * other threads waiting at that barrier point will also leave116 * abnormally via {@linkBrokenBarrierException} (or117 * {@linkInterruptedException} if they too were interrupted at about118 * the same time).119 *120 *

Memory consistency effects: Actions in a thread prior to calling121 * {@codeawait()}122 * happen-before123 * actions that are part of the barrier action, which in turn124 * happen-before actions following a successful return from the125 * corresponding {@codeawait()} in other threads.126 *127 *@since1.5128 *@seeCountDownLatch129 *130 *@authorDoug Lea131 */

132 public classCyclicBarrier {133 /**

134 * Each use of the barrier is represented as a generation instance.135 * The generation changes whenever the barrier is tripped, or136 * is reset. There can be many generations associated with threads137 * using the barrier - due to the non-deterministic way the lock138 * may be allocated to waiting threads - but only one of these139 * can be active at a time (the one to which count applies)140 * and all the rest are either broken or tripped.141 * There need not be an active generation if there has been a break142 * but no subsequent reset.143 */

144 private static classGeneration {145 boolean broken = false;146 }147

148 /**The lock for guarding barrier entry*/

149 private final ReentrantLock lock = newReentrantLock();150 /**Condition to wait on until tripped*/

151 private final Condition trip =lock.newCondition();152 /**The number of parties*/

153 private final intparties;154 /*The command to run when tripped*/

155 private finalRunnable barrierCommand;156 /**The current generation*/

157 private Generation generation = newGeneration();158

159 /**

160 * Number of parties still waiting. Counts down from parties to 0161 * on each generation. It is reset to parties on each new162 * generation or when broken.163 */

164 private intcount;165

166 /**

167 * Updates state on barrier trip and wakes up everyone.168 * Called only while holding lock.169 */

170 private voidnextGeneration() {171 //signal completion of last generation

172 trip.signalAll();173 //set up next generation

174 count =parties;175 generation = newGeneration();176 }177

178 /**

179 * Sets current barrier generation as broken and wakes up everyone.180 * Called only while holding lock.181 */

182 private voidbreakBarrier() {183 generation.broken = true;184 count =parties;185 trip.signalAll();186 }187

188 /**

189 * Main barrier code, covering the various policies.190 */

191 private int dowait(boolean timed, longnanos)192 throwsInterruptedException, BrokenBarrierException,193 TimeoutException {194 final ReentrantLock lock = this.lock;195 lock.lock();196 try{197 final Generation g =generation;198

199 if(g.broken)200 throw newBrokenBarrierException();201

202 if(Thread.interrupted()) {203 breakBarrier();204 throw newInterruptedException();205 }206

207 int index = --count;208 if (index == 0) { //tripped

209 boolean ranAction = false;210 try{211 final Runnable command =barrierCommand;212 if (command != null)213 command.run();214 ranAction = true;215 nextGeneration();216 return 0;217 } finally{218 if (!ranAction)219 breakBarrier();220 }221 }222

223 //loop until tripped, broken, interrupted, or timed out

224 for(;;) {225 try{226 if (!timed)227 trip.await();228 else if (nanos > 0L)229 nanos =trip.awaitNanos(nanos);230 } catch(InterruptedException ie) {231 if (g == generation && !g.broken) {232 breakBarrier();233 throwie;234 } else{235 //We're about to finish waiting even if we had not236 //been interrupted, so this interrupt is deemed to237 //"belong" to subsequent execution.

238 Thread.currentThread().interrupt();239 }240 }241

242 if(g.broken)243 throw newBrokenBarrierException();244

245 if (g !=generation)246 returnindex;247

248 if (timed && nanos <= 0L) {249 breakBarrier();250 throw newTimeoutException();251 }252 }253 } finally{254 lock.unlock();255 }256 }257

258 /**

259 * Creates a new CyclicBarrier that will trip when the260 * given number of parties (threads) are waiting upon it, and which261 * will execute the given barrier action when the barrier is tripped,262 * performed by the last thread entering the barrier.263 *264 *@paramparties the number of threads that must invoke {@link#await}265 * before the barrier is tripped266 *@parambarrierAction the command to execute when the barrier is267 * tripped, or {@codenull} if there is no action268 *@throwsIllegalArgumentException if {@codeparties} is less than 1269 */

270 public CyclicBarrier(intparties, Runnable barrierAction) {271 if (parties <= 0) throw newIllegalArgumentException();272 this.parties =parties;273 this.count =parties;274 this.barrierCommand =barrierAction;275 }276

277 /**

278 * Creates a new CyclicBarrier that will trip when the279 * given number of parties (threads) are waiting upon it, and280 * does not perform a predefined action when the barrier is tripped.281 *282 *@paramparties the number of threads that must invoke {@link#await}283 * before the barrier is tripped284 *@throwsIllegalArgumentException if {@codeparties} is less than 1285 */

286 public CyclicBarrier(intparties) {287 this(parties, null);288 }289

290 /**

291 * Returns the number of parties required to trip this barrier.292 *293 *@returnthe number of parties required to trip this barrier294 */

295 public intgetParties() {296 returnparties;297 }298

299 /**

300 * Waits until all {@linkplain#getParties parties} have invoked301 * await on this barrier.302 *303 *

If the current thread is not the last to arrive then it is304 * disabled for thread scheduling purposes and lies dormant until305 * one of the following things happens:306 *

  • 307 *
  • The last thread arrives; or308 *
  • Some other thread {@linkplainThread#interrupt interrupts}309 * the current thread; or310 *
  • Some other thread {@linkplainThread#interrupt interrupts}311 * one of the other waiting threads; or312 *
  • Some other thread times out while waiting for barrier; or313 *
  • Some other thread invokes {@link#reset} on this barrier.314 *
315 *316 *

If the current thread:317 *

  • 318 *
  • has its interrupted status set on entry to this method; or319 *
  • is {@linkplainThread#interrupt interrupted} while waiting320 *
321 * then {@linkInterruptedException} is thrown and the current thread's322 * interrupted status is cleared.323 *324 *

If the barrier is {@link#reset} while any thread is waiting,325 * or if the barrier {@linkplain#isBroken is broken} when326 * await is invoked, or while any thread is waiting, then327 * {@linkBrokenBarrierException} is thrown.328 *329 *

If any thread is {@linkplainThread#interrupt interrupted} while waiting,330 * then all other waiting threads will throw331 * {@linkBrokenBarrierException} and the barrier is placed in the broken332 * state.333 *334 *

If the current thread is the last thread to arrive, and a335 * non-null barrier action was supplied in the constructor, then the336 * current thread runs the action before allowing the other threads to337 * continue.338 * If an exception occurs during the barrier action then that exception339 * will be propagated in the current thread and the barrier is placed in340 * the broken state.341 *342 *@returnthe arrival index of the current thread, where index343 * {@link#getParties()} - 1 indicates the first344 * to arrive and zero indicates the last to arrive345 *@throwsInterruptedException if the current thread was interrupted346 * while waiting347 *@throwsBrokenBarrierException if another thread was348 * interrupted or timed out while the current thread was349 * waiting, or the barrier was reset, or the barrier was350 * broken when {@codeawait} was called, or the barrier351 * action (if present) failed due an exception.352 */

353 public int await() throwsInterruptedException, BrokenBarrierException {354 try{355 return dowait(false, 0L);356 } catch(TimeoutException toe) {357 throw new Error(toe); //cannot happen;

358 }359 }360

361 /**

362 * Waits until all {@linkplain#getParties parties} have invoked363 * await on this barrier, or the specified waiting time elapses.364 *365 *

If the current thread is not the last to arrive then it is366 * disabled for thread scheduling purposes and lies dormant until367 * one of the following things happens:368 *

  • 369 *
  • The last thread arrives; or370 *
  • The specified timeout elapses; or371 *
  • Some other thread {@linkplainThread#interrupt interrupts}372 * the current thread; or373 *
  • Some other thread {@linkplainThread#interrupt interrupts}374 * one of the other waiting threads; or375 *
  • Some other thread times out while waiting for barrier; or376 *
  • Some other thread invokes {@link#reset} on this barrier.377 *
378 *379 *

If the current thread:380 *

  • 381 *
  • has its interrupted status set on entry to this method; or382 *
  • is {@linkplainThread#interrupt interrupted} while waiting383 *
384 * then {@linkInterruptedException} is thrown and the current thread's385 * interrupted status is cleared.386 *387 *

If the specified waiting time elapses then {@linkTimeoutException}388 * is thrown. If the time is less than or equal to zero, the389 * method will not wait at all.390 *391 *

If the barrier is {@link#reset} while any thread is waiting,392 * or if the barrier {@linkplain#isBroken is broken} when393 * await is invoked, or while any thread is waiting, then394 * {@linkBrokenBarrierException} is thrown.395 *396 *

If any thread is {@linkplainThread#interrupt interrupted} while397 * waiting, then all other waiting threads will throw {@link

398 * BrokenBarrierException} and the barrier is placed in the broken399 * state.400 *401 *

If the current thread is the last thread to arrive, and a402 * non-null barrier action was supplied in the constructor, then the403 * current thread runs the action before allowing the other threads to404 * continue.405 * If an exception occurs during the barrier action then that exception406 * will be propagated in the current thread and the barrier is placed in407 * the broken state.408 *409 *@paramtimeout the time to wait for the barrier410 *@paramunit the time unit of the timeout parameter411 *@returnthe arrival index of the current thread, where index412 * {@link#getParties()} - 1 indicates the first413 * to arrive and zero indicates the last to arrive414 *@throwsInterruptedException if the current thread was interrupted415 * while waiting416 *@throwsTimeoutException if the specified timeout elapses417 *@throwsBrokenBarrierException if another thread was418 * interrupted or timed out while the current thread was419 * waiting, or the barrier was reset, or the barrier was broken420 * when {@codeawait} was called, or the barrier action (if421 * present) failed due an exception422 */

423 public int await(longtimeout, TimeUnit unit)424 throwsInterruptedException,425 BrokenBarrierException,426 TimeoutException {427 return dowait(true, unit.toNanos(timeout));428 }429

430 /**

431 * Queries if this barrier is in a broken state.432 *433 *@return{@codetrue} if one or more parties broke out of this434 * barrier due to interruption or timeout since435 * construction or the last reset, or a barrier action436 * failed due to an exception; {@codefalse} otherwise.437 */

438 public booleanisBroken() {439 final ReentrantLock lock = this.lock;440 lock.lock();441 try{442 returngeneration.broken;443 } finally{444 lock.unlock();445 }446 }447

448 /**

449 * Resets the barrier to its initial state. If any parties are450 * currently waiting at the barrier, they will return with a451 * {@linkBrokenBarrierException}. Note that resets after452 * a breakage has occurred for other reasons can be complicated to453 * carry out; threads need to re-synchronize in some other way,454 * and choose one to perform the reset. It may be preferable to455 * instead create a new barrier for subsequent use.456 */

457 public voidreset() {458 final ReentrantLock lock = this.lock;459 lock.lock();460 try{461 breakBarrier(); //break the current generation

462 nextGeneration(); //start a new generation

463 } finally{464 lock.unlock();465 }466 }467

468 /**

469 * Returns the number of parties currently waiting at the barrier.470 * This method is primarily useful for debugging and assertions.471 *472 *@returnthe number of parties currently blocked in {@link#await}473 */

474 public intgetNumberWaiting() {475 final ReentrantLock lock = this.lock;476 lock.lock();477 try{478 return parties -count;479 } finally{480 lock.unlock();481 }482 }483 }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值