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

问题描述 投票:0回答:1

我必须在其他系统上调用操作。另一个系统是高度并发和分布式的。因此,我将其集成到MessageBus上。需要一种实现方式来让调用方等待,直到在总线上收到结果或超时到期为止。该实现包括三个步骤:首先,我必须将future和CountDownLatch打包到ConcurrentHashMap中,CountDownLatch在收到结果后立即释放。该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替换为共享对象上的wait / notify组合解决了该问题,而无需进一步的同步(当然会超时)

java concurrency synchronization wait countdownlatch
1个回答
0
投票

如果最终结果保持不变(就编译器的想法而言,可能会发生指令重新排序,如果涉及多个线程,则可能需要额外的同步(因为编译器不知道其他线程可能期望特定的顺序)。

[在您的示例中,在单个线程中,所有步骤之间都具有happens-before关系。即使编译器可以对这些方法调用进行重新排序,最终结果也必须相同(在execute()之前调用的await())。由于await()涉及同步,所以除非您使用的是[[buggy疯狂的实现,否则execute()不可能以某种方式滑倒它。

happens-beforecountDown()之间的await()关系确保在执行PendingExecution之前发生await()代码。因此,显示的代码没有错。

[您应该

总是比java.util.concurrent更喜欢wait/notify类,因为它们更易于使用并且提供了更多功能。

© www.soinside.com 2019 - 2024. All rights reserved.