【问题标题】:CompletableFuture runAsync vs new ThreadCompletableFuture runAsync 与新线程
【发布时间】:2018-12-11 19:12:15
【问题描述】:

上下文:我读过 this SO 线程讨论 CompletableFuture 和线程之间的区别。

但我想了解何时我应该使用 new Thread() 而不是 runAsync()。

我知道 runAsyn() 对于短期/一次性并行任务更有效,因为我的程序可能会避免创建全新线程的开销,但我是否也应该考虑将它用于长时间运行的操作?

在考虑使用其中一种之前,我应该注意哪些因素?

谢谢大家。

【问题讨论】:

    标签: java multithreading asynchronous concurrency java-8


    【解决方案1】:

    使用低级并发 API(如 Thread)与其他 API 的区别不仅在于您想要完成的工作类型,还在于编程模型以及它们使配置变得多么容易任务运行的环境。

    一般来说,建议使用更高级别的 API,例如 CompletableFuture,而不是直接使用 Threads。

    我知道 runAsyn() 对于短期/一次性并行任务更有效

    好吧,也许,假设您调用runAsync,指望它使用分叉连接池。 runAsync 方法重载了一个方法,该方法允许指定执行异步任务的 java.util.concurrent.Executor

    使用ExecutorService 对线程池进行更多控制,以及将CompletableFuture.runAsyncCompletableFuture.supplyAsync 与指定的执行程序服务一起使用(或不使用)通常都优于直接创建和运行Thread 对象。

    没有什么特别赞成或反对将 CompletableFuture API 用于长时间运行的任务。但是选择使用Threads 也有其他含义,其中:

    • CompletableFuture 为响应式编程提供了更好的 API,而无需强制我们编写显式同步代码。 (直接使用线程时我们不会得到这个)
    • Future 接口(由CompletableFuture 实现)提供了其他明显的优势。

    因此,简而言之:您可以(并且可能应该,如果正在考虑的替代方案是Thread API)为您的长期运行任务使用CompletableFuture API。为了更好地控制线程池,可以将其与执行器服务结合使用。

    【讨论】:

    • 感谢您的回答。那么,有什么好的理由显式地创建一个线程吗?从你所说的来看,它总是建议使用线程池。
    • @TheApprentice 当然有充分的理由,但我试图传达的想法是,只要有可能,人们应该更喜欢更高级别的 API。在某些情况下,需要在线程类中发挥作用,例如通过扩展类......无论如何,当有必要时,它是适当的。如果不是,其他 API 更适合。
    • 但是这些好的理由到底是什么?你能举个例子吗?我试图了解何时适合使用显式线程而不是使用高级 API。谢谢。
    • @TheApprentice 这正是挑战所在:这是设计代码的开发人员的选择。没有明确的规则,但可以简化为“首先尝试高级并发工具包,作为首选,当它们变得不充分时,你就会知道。你可能需要自定义低级信息,例如线程名称等。换句话说,开发者有选择权,但也要负责权衡”
    • @Kjeld 一个线程一次将执行一项任务。但是当第一个任务完成时,它将被重新用于其他任务。您可以通过以不同的模式加载执行程序并在任务中打印线程名称来测试这一点。
    【解决方案2】:

    主要区别在于CompletableFuture 默认在ForkJoinPool.commonPool 上运行您的任务。但是,如果您创建自己的线程并启动它,它将作为单个线程执行,而不是在线程池上。另外,如果您想按顺序执行某些任务,但asynchronously。然后你可以像下面那样做。

        CompletableFuture.runAsync(() -> {
            System.out.println("On first task");
            System.out.println("Thread : " + Thread.currentThread());
        }).thenRun(() -> {
            System.out.println("On second task");
        });
    

    输出:

    On first task
    Thread : Thread[ForkJoinPool.commonPool-worker-1,5,main]
    On second task
    

    如果你运行上面的代码,你可以看到CompletableFuture正在使用哪个池。

    注意:线程是ForkJoinPool.commonPool 中的守护进程。

    【讨论】:

    • 我的问题不是如何使用 runAsync() 或它是如何工作的,因为我已经知道了。我更关心设计原则,何时使用其中一种。我知道一个使用线程池,另一个不使用。但无论哪种方式,感谢您的时间。
    猜你喜欢
    • 2018-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多