我有一个实用程序方法(用于单元测试,确实如此),它在另一个线程中执行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.get()
上调用Future
之后,方法的调用者是否可以安全地假定Runnable
已完成执行,并且其结果已安全发布?
答案 0 :(得分:3)
否,您不需要join()
。将来致电get()
就足够了。
CompletableFuture
接口是Future
的子类型。 Future
的{{3}}指出:
内存一致性影响:在另一个线程中,相应的
Future.get()
之后的异步计算 happen-before 操作采取的操作。
先发生关系足以确保安全发布get()
返回的值。
此外,get()
的调用在CompletableFuture
完成,异常完成或取消之前不会完成。
答案 1 :(得分:1)
如果我们看一下Shipilev的Safe Publication,那么获得安全发表的简单方法之一就是工作:
通过volatile字段(JLS 17.4.5)或通过此规则通过AtomicX类交换引用
由于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.