Search code examples
javaconcurrencysynchronizationwaitcountdownlatch

Java - Can CountDownLatch.await() be reordered by the compiler


I have to invoke an operation on a different system. The other system is highly concurrent and distributed. Therefore I integrate it over a MessageBus. One implementation is required to let the caller wait until the result got received on the bus or the timeout expires. The implementation consists of three steps: First I have to pack the future and the CountDownLatch, which is released once the result is received, into a ConcurrentHashMap. This Map is shared with the component listening on the MessageBus. Then I have to start the execution and finally wait on the latch.

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;
}

So the question is: Can those 3 Steps be reordered by the compiler? As far as I understand, only Step 1 and Step 3 form a happens-before relationship. So in theory step 2 could be freely moved by the compiler. Could step 2 even be moved behind the await and therefore would invalidate the code?

Follow-up question: Would replacing the CountDownLatch with a wait/notify combination on a shared object solve the problem without further synchronization (leaving out the timeout of course)


Solution

  • Instruction reordering can happen if the end result remains the same (as far as the compiler thinks), and may require additional synchronization if multiple threads are involved (since the compiler doesn't know that other threads may expect a specific order).

    In a single thread, as in your example, all steps have a happens-before relationship with each other. Even if the compiler could reorder those method invocations, the end result needs to be the same (execute() called before await()). As await() involves synchronization, there's no way that execute() could somehow slip after it, unless you were using a buggy crazy implementation.

    The happens-before relationship between countDown() and await() makes sure that PendingExecution code happens before code executed await(). So there's nothing wrong with the code shown.

    You should always prefer the java.util.concurrent classes over wait/notify, as they're easier to use and provide a lot more functionality.