您必须加入线程以确保其计算完成

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

我有一个实用程序方法(用于单元测试,碰巧)在另一个线程中执行 Runnable。它启动线程 运行,但不等待 Thread 完成,而是依赖 Future。该方法的调用者应 get() Future。但这足以确保安全发布 Runnable 所做的计算吗?

方法如下:

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

在返回的 Future 上调用 Future.get() 后,方法的调用者是否可以安全地假设 Runnable 已完成执行,并且其结果已安全发布?

如果我们看看 Shipilev 的 Safe Publication,获得安全发布的一种简单方法就是工作:

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

由于 CompletableFuture 使用 volatile 字段来写入和读取值,因此安全发布不需要额外的内存屏障。这在 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.

它还处理已发布对象的安全初始化,根据稍后的相同概述评论:

 * Completion fields need not be declared as final or volatile
 * because they are only visible to other threads upon safe
 * publication.

不,你不需要 join()。对未来调用 get() 就足够了。

CompletableFuture 接口是 Future 的子类型。 Futurejavadoc 表示:

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

happen-before 关系足以确保安全发布 get().

返回的值

此外,在 CompletableFuture 完成、异常完成或取消之前,get() 调用不会完成。