Must you join on a Thread to ensure its computation is complete
Asked Answered
V

2

6

I have a utility method (used for unit testing, it so happens) that executes a Runnable in another thread. It starts the thread running, but does not wait for the Thread to finish, instead relying on a Future. A caller of the method is expected to get() that Future. But is that enough to ensure safe publication of the computation done by the Runnable?

Here is the method:

private static Future<Void> runInOtherThread(final CountDownLatch ready, final Runnable operation) {
    final CompletableFuture<Void> future = new CompletableFuture<Void>();
    final Thread thread = new Thread(() -> {
        try {
            ready.await();
            operation.run();
        } catch (Throwable e) {
            future.completeExceptionally(e);
            return;
        }
        future.complete(null);
    });
    thread.start();
    return future;
}

After calling Future.get() on the returned Future, can the caller of the method safely assume that the Runnable has finished execution, and its results have been safely published?

Voltz answered 11/12, 2018 at 23:10 Comment(5)
Since Future#get blocks until the operation completes (or fails) that I would suggest yes - but I might be missing something in the questionManque
@Manque Yes, I think the answer to my question is "yes, the computation is complete and safely published", but multithreading is full of surprises and traps for the unwary, so I'm asking to check.Voltz
I feel your painManque
Interesting! The answer for any everyday use-case is yes, but if the question is the if in JVM it is deterministic to always guarantee the completion. Then I would say no, as theoretically it is still a side-effect. I will follow the question to see what guarantees JVM providesBilbe
Yes, it guarantees it: docs.oracle.com/javase/8/docs/api/index.html?java/util/…: Actions taken by the asynchronous computation represented by a Future happen-before actions subsequent to the retrieval of the result via Future.get() in another thread.Sukiyaki
S
4

No you don't need to join(). Calling get() on the future is sufficient.

The CompletableFuture interface is a subtype of Future. And the javadoc for Future states this:

Memory consistency effects: Actions taken by the asynchronous computation happen-before actions following the corresponding Future.get() in another thread.

That happen-before relationship is sufficient to ensure safe publication of the value returned by get().

Furthermore, the get() call will not complete until the CompletableFuture has been completed, exceptionally-completed or cancelled.

Spectator answered 12/12, 2018 at 1:3 Comment(0)
D
2

If we look at Safe Publication by Shipilev one of the trivial ways to get safe publication is to work:

Exchange the reference via a volatile field (JLS 17.4.5), or as the consequence of this rule, via the AtomicX classes

Since CompletableFuture uses a volatile field to write and read the value no additional memory barriers are necessary for safe publication. This is explained in CompletableFuture class overview comment:

 * A CompletableFuture may have dependent completion actions,
 * collected in a linked stack. It atomically completes by CASing
 * a result field, and then pops off and runs those actions. This
 * applies across normal vs exceptional outcomes, sync vs async
 * actions, binary triggers, and various forms of completions.
 *
 * Non-nullness of volatile field "result" indicates done.  It may
 * be set directly if known to be thread-confined, else via CAS.
 * An AltResult is used to box null as a result, as well as to
 * hold exceptions.

It also handles the safe initialization of the published objects, as per the same overview comment later:

 * Completion fields need not be declared as final or volatile
 * because they are only visible to other threads upon safe
 * publication.
Dendroid answered 11/12, 2018 at 23:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.