倒计数锁存器(CountDown Latch)是异常性障碍,允许一个或多个线程等待一个或者多个其他线程来做某些事情。
public static long time(Executor executor,int concurrency,final Runnable action)throws InterruptedException{ //CountDownLatch 构造器参数代表 只有调用countDown()方法达到这个int值的时候,才能继续(所以多线程的情况下可以在此阻塞) final CountDownLatch ready=new CountDownLatch(concurrency); final CountDownLatch start=new CountDownLatch(1); final CountDownLatch done=new CountDownLatch(concurrency); for (int i=0;i<concurrency;i++){ executor.execute(new Runnable() { @Override public void run() { ready.countDown();//准备好了一个就++1 try{ start.await();//等待所有for循环跑完 主线程执行countDown的时候才能继续走 action.run(); }catch (InterruptedException ex){ Thread.currentThread().interrupt(); }finally{ done.countDown();//线程执行完了才能++ } } }); } ready.await(); long startNanos=System.nanoTime(); start.countDown(); done.await();//阻塞等待异步线程的countDown()调用次数达到阀值 return System.nanoTime()-startNanos; }
对于间歇式的定时,始终应该优先使用System.nanoTime()而不是System.currentTimeMills,前者更加准确更加精确并且不受系统的实时时钟的调整影响。
三个CountdownLatch也可以用CyclicBarrier(同步屏障)代替。
public static long time1(Executor executor, int concurrency, final Runnable action) { CyclicBarrier cyclicBarrier = new CyclicBarrier(concurrency); long startNanos = System.nanoTime(); for (int i = 0; i < concurrency; i++) { executor.execute(new Runnable() { @Override public void run() { try { action.run(); } finally { try { cyclicBarrier.await(); } catch (BrokenBarrierException ex) { } catch (InterruptedException ex) { } } } }); } return System.nanoTime() - startNanos; }
CountDownLatch的计数器只能使用一次。而CyclicBarrier的计数器可以使用reset() 方法重置。所以CyclicBarrier能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次。
CyclicBarrier还提供其他有用的方法,比如getNumberWaiting方法可以获得CyclicBarrier阻塞的线程数量。isBroken方法用来知道阻塞的线程是否被中断。
总的来说就是可以同步等待所有异步任务完成后进行汇总操作。