繁体   English   中英

Java - 编译器可以重新排序 CountDownLatch.await()

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM