CompletableFuture 显示不一致的线程行为

CompletableFuture showing inconsistent threading behavior

CompletableFuture 的文档指定了异步执行的以下行为:

All async methods without an explicit Executor argument are performed using the ForkJoinPool#commonPool() (unless it does not support a parallelism level of at least two, in which case, a new Thread is created to run each task). To simplify monitoring, debugging, and tracking, all generated asynchronous tasks are instances of the marker interface AsynchronousCompletionTask.

然而,同步(或至少非异步)方法的行为仍不清楚。在大多数情况下,代码会使用原始线程执行,如下所示:

Thread thread = Thread.currentThread();
CompletableFuture.runAsync(() -> {
        // Should run on the common pool - working as expected
        assert thread != Thread.currentThread();
}).thenRun(() -> {
        // Returns to running on the thread that launched.. sometimes?
        assert thread == Thread.currentThread();
}).join();

但是,重复此测试会产生不一致的结果,因为第二个代码块有时会改用公共池。这种情况下的预期行为是什么?

在查看了 CompletableFuture here 的 OpenJDK 实现后,您似乎遇到了某种竞争条件。假设我们正在按照 runAsync(a).thenRun(b) 的方式做某事。如果 athenRun(b) 在当前线程上被调用之前完成公共池线程上的执行,那么 b 是 运行 立即在当前线程上一次 thenRun(b) finally被调用。另一方面,如果 thenRun(b)a 在另一个线程上完成之前被调用,那么 b 在与 a 相同的线程上 运行 一旦 a终于完成了。

有点像这样:

class CompletableFuture:
  function runAsync(a):
    function c():
      run a
      for each b in the handler stack:
        run b
    run c on a different thread
    return future representing c
  function thenRun(b):
    if c is done:
      run b
    else:
      put b in c's handler stack

显然,如果在 a 完成之前调用 thenRun,则 bc 的线程中是 运行。否则,b在当前线程中是运行。

如果您希望在哪个线程 运行 和什么方面有更一致的行为,您应该尝试使用 CompletableFuture#thenRunAsync,它保证处理程序在某个特定的执行程序池中执行。