[英]Java - Can CountDownLatch.await() be reordered by the compiler
我必须在不同的系统上调用操作。 另一个系统是高度并发和分布式的。 因此,我将它集成到 MessageBus 上。 需要一种实现来让调用者等待,直到总线上收到结果或超时到期。 该实现包括三个步骤:首先,我必须将future 和CountDownLatch(一旦收到结果就会释放)打包到ConcurrentHashMap 中。 此 Map 与侦听 MessageBus 的组件共享。 然后我必须开始执行,最后等待闩锁。
public FailSafeFuture execute(Execution execution,long timeout,TimeUnit timeoutUnit) {
//Step 1
final WaitingFailSafeFuture future = new WaitingFailSafeFuture();
final CountDownLatch countDownLatch = new CountDownLatch(1);
final PendingExecution pendingExecution = new PendingExecution(future, countDownLatch);
final String id = execution.getId();
pendingExecutions.put(id, pendingExecution); //ConcurrentHashMap shared with Bus
//Step 2
execution.execute();
//Step 3
final boolean awaitSuccessfull = countDownLatch.await(timeout, timeoutUnit);
//...
return future;
}
所以问题是:编译器可以重新排序这 3 个步骤吗? 据我了解,只有第 1 步和第 3 步形成了先发生的关系。 所以理论上步骤 2 可以由编译器自由移动。 甚至可以将步骤 2 移到等待之后,从而使代码无效吗?
后续问题:将 CountDownLatch 替换为共享 object 上的等待/通知组合可以解决问题而无需进一步同步(当然忽略超时)
如果最终结果保持不变(就编译器的想法而言),指令重新排序可能会发生,并且如果涉及多个线程(因为编译器不知道其他线程可能期望特定的顺序),则可能需要额外的同步。
在单个线程中,如您的示例所示,所有步骤彼此之间都有happens-before
关系。 即使编译器可以重新排序这些方法调用,最终结果也需要相同(在await()
之前调用execute()
) )。 由于await()
涉及同步,因此execute()
不可能以某种方式滑过它,除非您使用的是
错误
的疯狂实现。
countDown()
和await()
之间happens-before
关系确保PendingExecution
代码在代码执行await()
之前发生。 所以显示的代码没有任何问题。
您应该始终更喜欢java.util.concurrent
类而不是wait/notify
,因为它们更易于使用并提供更多功能。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.