为何CyclicBarrier.await(int timeout, TimeUnit unit)未抛出TimeoutException?
首先直接点出核心误解:你对await(int timeout, TimeUnit unit)的超时逻辑理解错了——这个超时时间是从当前线程调用await方法的那一刻开始计算的,而不是从某个全局的启动时间点开始算。
我们来拆解你的代码里的时间线(以主线程启动时间为T=0秒):
T=0秒:主线程启动线程0、1、2、3,这四个线程立刻进入
run方法,先执行TimeUnit.SECONDS.sleep(2)(业务代码),也就是会在T=2秒左右结束睡眠,然后调用cyclicBarrier.await(3, TimeUnit.SECONDS)。此时这四个线程开始等待屏障,它们的超时截止时间是T=2+3=5秒。T=0秒到T=2秒:主线程在循环到i=4时,执行
TimeUnit.SECONDS.sleep(2),也就是在T=2秒的时候启动线程4。T=2秒到T=4秒:线程4启动后,同样先执行
TimeUnit.SECONDS.sleep(2),在T=4秒左右结束睡眠,然后调用cyclicBarrier.await(...)。
这时候关键来了:线程4在T=4秒的时候到达屏障,此时距离线程0-3开始等待的时间只过去了2秒,远小于它们设置的3秒超时时间!所以所有5个线程都凑齐了,屏障直接触发,执行了构造时的Runnable,然后所有线程继续执行后续代码,自然不会抛出TimeoutException。
验证你的预期的话,怎么改?
如果想让线程0-3触发超时,你需要让线程4到达屏障的时间晚于T=5秒。比如把主线程中启动线程4前的sleep改成4秒:
// 主线程i=4的分支 try { TimeUnit.SECONDS.sleep(4); // 改成4秒 new MyCyclicBarrier(cyclicBarrierWithRunnable).start(); } catch (InterruptedException e) { e.printStackTrace(); }
这样线程4会在T=4+2=6秒的时候到达屏障,而线程0-3的超时时间是T=5秒,到了T=5秒的时候,这四个线程就会抛出TimeoutException,同时屏障会被打破(后续到达的线程也会触发BrokenBarrierException)。
总结
CyclicBarrier.await的超时是线程级别的等待时长:每个线程调用await后,开始计时,如果在这个时长内还没凑齐所有参与方,才会抛出超时异常。不是从第一个线程到达屏障开始计时,也不是从主线程启动开始计时。
内容的提问来源于stack exchange,提问作者KevinWyen

