Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 596
Raedwald Avatar asked Dec 11 '18 23:12

Raedwald


2 Answers

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.

like image 58
Stephen C Avatar answered Nov 04 '22 01:11

Stephen C


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.
like image 38
Karol Dowbecki Avatar answered Nov 04 '22 01:11

Karol Dowbecki