线程 vs CompletableFuture

37
直接将代码传递到线程中与使用 CompletableFuture 相比有什么优势?
Thread thread = new Thread(() -> {do something});
thread.start();

VS
CompletableFuture<Void> cf1 =  
     CompletableFuture.runAsync(() -> {do something}); 
3个回答

41

CompletableFuture.runAsync(...)会在由系统管理的ForkJoin线程池中运行Runnable,而new Thread()则会创建一个新线程,需要你自己来管理。

"is managed"是什么意思呢?它意味着预分配了线程并共享在JVM中。当Runnable完成时,该线程可以被重用于其他Runnable。这样可以更好地利用资源,特别是线程实例化是昂贵的操作-不仅需要分配对象,还要分配一些额外的非堆内存(即线程栈)。


如果我采用thread.start方法,还需要进行哪些管理工作? - Sagar
4
不需要管理它。您可以创建一个新线程,启动它,然后忘记它。除了创建和销毁新线程的成本之外,没有任何惩罚。 - Solomon Slow
@jameslarge 谢谢!我从来没有直接使用过线程,总是使用 Executors,所以不知道那个。 - Sagar
@James,你说得对,“管理”任何意义上的“管理”,创建已经在管理了,但你也可以中断它,给它一个名称或将其放入线程组中,当然你也可以做所有被弃用和不鼓励使用的事情,如stopsuspendresume。当然,除了创建之外,你不必做任何事情,但如果你热衷于低级线程处理(关注“如何”而不是“什么”),你也可以这样做。对于CompletableFuture,你不能这样做,因为所有的工作都已经为你完成,你操作的是高级API并发(关注“什么”而不是“如何”)。 - Gerald Mücke
1
@jameslarge “除了创建和销毁的成本之外,没有任何惩罚”,你让它听起来好像每个人都拥有无限的核心和运行线程不需要任何代价。实际上,它会消耗 CPU 时间,这也是使用池的原因之一。 - Cargeh
5
这里的一些声明似乎有些过于概括(@Cargeh和james large)。线程不仅用于利用可用的处理资源(核心),而且还用于隐藏等待时间或执行非阻塞操作 -即使(新的)线程主要是在等待(例如等待IO)。拥有一个池,其中有n个线程全部都在等待某个(不存在的)n+1个线程应该做的事情是没有意义的。当然,这种情况通常被抽象层和库隐藏起来。但是,手动创建新线程可能是完全可以的-即使您是“有经验”的人。 - Marco13

12

@Gerald Mücke已经提到了重要的区别:

CompletableFuture.runAsync(...)在受管理的forkJoin-Pool中运行Runnable,而new Thread()创建一个需要您自己管理的新线程。

CompletableFuture将使用由线程池(默认或自定义)管理的线程。

然而,我认为以下两点也应该被考虑。

第一

CompletableFuture具有许多易于理解的方法,可以链接不同的异步计算,使其比直接使用Thread更容易引入异步性。

CompletableFuture[] futures = IntStream.rangeClosed(0, LEN).boxed()
            .map(i -> CompletableFuture.supplyAsync(() -> runStage1(i), EXECUTOR_SERVICE))
            .map(future -> future.thenCompose(i -> CompletableFuture.supplyAsync(() -> runStage2(i), EXECUTOR_SERVICE)))
            .toArray(size -> new CompletableFuture[size]);
CompletableFuture.allOf(futures).join();

第二点

您不应忘记处理异常;使用CompletableFuture,您可以直接像这样处理它们:

completableFuture.handle((s, e) -> e.toString()).join()

或者可以利用它们来中断计算:

completableFuture.completeExceptionally(new RuntimeException("Calculation failed!"));

使用Thread时,您很容易遇到一些严重的问题。


1

CompletableFuture是一种承诺(Promise),它使用默认的ForkJoinPool(线程池大小等于CPU数量),除非提供另一个ThreadPool。线程池将包含n个或更多个线程。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接