使用场景
有若干个线程,比如有4个线程,需要他们到达某一个点之后才能开始一起执行,假如有3个线程到达了这个点,还差1个线程没到达,此时3个线程都会进入等待状态,直到第4个线程也到达这个点之后,这4个线程才开始一起进行执行状态。
示例:
public class CyclicBarrierTest { public static void main(String[] args) throws BrokenBarrierException, InterruptedException { CyclicBarrier cyclicBarrier = new CyclicBarrier(4); new Thread( () -> { try { Thread.sleep(1000); System.out.println(System.currentTimeMillis() + " 线程1,开始准备。。。"); cyclicBarrier.await(); System.out.println(System.currentTimeMillis() + " 线程1,准备好了。。。"); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } } ).start(); new Thread( () -> { try { Thread.sleep(2000); System.out.println(System.currentTimeMillis() + " 线程2,开始准备。。。"); cyclicBarrier.await(); System.out.println(System.currentTimeMillis() + " 线程2,准备好了。。。"); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } } ).start(); new Thread( () -> { try { Thread.sleep(3000); System.out.println(System.currentTimeMillis() + " 线程3,开始准备。。。"); cyclicBarrier.await(); System.out.println(System.currentTimeMillis() + " 线程3,准备好了。。。"); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } } ).start(); System.out.println(System.currentTimeMillis() + " 主线程,开始准备。。。"); cyclicBarrier.await(); System.out.println(System.currentTimeMillis() + " 主线程,准备好了。。。"); } }
执行结果,4个线程都到达一个点之后一起执行:
源码分析
CountDownLatch是基于AQS实现的;而CyclicBarrier是基于ReentrantLock重入锁实现的,当然ReentrantLock也是基于AQS实现的,非要说CyclicBarrier也是基于AQS实现的也不为过。
await()方法:
-
先检查前面是否已经有count个线程了,如果没有线程则会进入等待状态
-
当检测到屏障已经有count个线程了,则所有线程会冲出屏障继续执行(如果有Runnable参数的构造方法先执行汇总方法)
public int await() throws InterruptedException, BrokenBarrierException { try { return dowait(false, 0L); } catch (TimeoutException toe) { throw new Error(toe); // cannot happen } } 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(); if (Thread.interrupted()) { breakBarrier(); throw new InterruptedException(); } int index = --count; if (index == 0) { // tripped boolean ranAction = false; try { final Runnable command = barrierCommand; if (command != null) command.run(); ranAction = true; nextGeneration(); return 0; } finally { if (!ranAction) breakBarrier(); } } // loop until tripped, broken, interrupted, or timed out for (;;) { try { 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(); } }