Java CompletableFuture的thenApply和thenApplyAsync有什么区别?
假设我有以下代码:
CompletableFuture future = CompletableFuture.supplyAsync( () -> 0);
thenApply
案例:
future.thenApply( x -> x + 1 ) .thenApply( x -> x + 1 ) .thenAccept( x -> System.out.println(x));
这里的输出将是2.现在在thenApplyAsync
情况下:
future.thenApplyAsync( x -> x + 1 ) // first step .thenApplyAsync( x -> x + 1 ) // second step .thenAccept( x -> System.out.println(x)); // third step
我在这篇博客中读到,每个thenApplyAsync
都是在一个单独的线程中执行的,并且“同时”(这意味着在执行thenApplyAsyncs
之前,然后启动了thenApplyAsyncs
),如果是这样的话,如果是第一步,第二步的输入参数值是多少没做完?
如果不是第二步,第一步的结果会在哪里? 第三步将采取哪一步的结果?
如果第二步必须等待第一步的结果那么Async
的重点是什么?
这里x – > x + 1只是为了表明这一点,我想知道的是在非常长的计算情况下。
不同之处在于负责运行代码的Executor
。 CompletableFuture
上的每个运营商通常有3个版本。
-
thenApply(fn)
– 在调用它的CompleteableFuture
定义的线程上运行fn
,因此通常无法知道它将在何处执行。 如果结果已经可用,它可能会立即执行。 -
thenApplyAsync(fn)
– 在环境定义的执行程序上运行fn
,无论情况如何。 对于CompletableFuture
这通常是ForkJoinPool.commonPool()
。 -
thenApplyAsync(fn,exec)
– 在exec
上运行fn
。
最后结果是相同的,但调度行为取决于方法的选择。
这就是文档中有关CompletableFuture's
thenApplyAsync
:
返回一个新的CompletionStage,当该阶段正常完成时,使用此阶段的默认异步执行工具执行,该阶段的结果作为所提供函数的参数。
所以, thenApplyAsync
必须等待之前的thenApplyAsync's
结果:
在您的情况下,您首先执行同步工作,然后执行异步工作。 因此,第二个是异步的并不重要,因为它只在同步工作完成后启动。
我们把它换掉吧。 在某些情况下,将首先打印“异步结果:2”,在某些情况下,将首先打印“同步结果:2”。 这里有所不同,因为调用1和2都可以异步运行,在一个单独的线程上调用1,在另一个线程上调用2,这可能是主线程。
CompletableFuture future = CompletableFuture.supplyAsync(() -> 0); future.thenApplyAsync(x -> x + 1) // call 1 .thenApplyAsync(x -> x + 1) .thenAccept(x -> System.out.println("async result: " + x)); future.thenApply(x -> x + 1) // call 2 .thenApply(x -> x + 1) .thenAccept(x -> System.out.println("sync result:" + x));
我必须指出,名称thenApply
然后应用thenApplyAsync
是绝对可怕和令人困惑的。 thenApplyAsync
中没有任何内容比这些方法的契约中的thenApply
更异步。
差异与运行函数的线程有关。 提供给thenApply
的函数可以在任何线程上运行
- 通话
complete
- 在同一个实例上调用
thenApply
而thenApplyAsync
要么使用默认的Executor
(也称为。线程池),要么使用提供的Executor
。
这些函数的异步部分与异步操作最终调用complete
或completeExceptionally
的事实有关。 这个想法来自Javascript,它与multithreading无关。