【问题标题】:CompletableFuture not responding after worker thread send to sleepCompletableFuture 在工作线程发送到睡眠后没有响应
【发布时间】:2021-01-15 17:39:04
【问题描述】:

我正在尝试了解 ConsumableFuture。 基本上,我向 ConsumableFuture 提供一个任务,然后让运行该任务的工作线程休眠 2 秒。我希望工作线程在 2 秒后恢复执行并返回结果。

public class CompletableFutureDemo {

    public static void main(String[] args) {
        
        System.err.println("Application started");
        
        CompletableFuture
            .supplyAsync(()->work1())
            .thenAccept(op-> System.out.println(op));
        
        System.err.println("Application ended");
    }
    
    public static int work1() {
        System.out.println(Thread.currentThread().getName());
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("work1 called");
        return (int) (Math.random() * 100);
    }
}

输出:

Application started
ForkJoinPool.commonPool-worker-1
Application ended

为什么工作线程没有恢复?

但如果我从工作线程中删除睡眠语句,那么我会得到所需的输出。

Application started
ForkJoinPool.commonPool-worker-1
work1 called
Application ended
64

【问题讨论】:

  • 普通 fork-join 池使用守护线程。您的应用程序正在退出,因为主线程(在这种情况下只有非守护线程)在其他线程完成之前退出。
  • @Slaw 感谢您提供的信息。 Fork-join 默认使用守护线程?有什么办法可以防止它们标记为守护进程?

标签: java multithreading java-8 executorservice fork-join


【解决方案1】:

正如@Slaw 已经在评论中指出的那样,主线程在工作线程休眠时完成并退出应用程序,因此您可以调用join 让主线程等待直到工作线程完成

System.err.println("Application started");

 CompletableFuture
            .supplyAsync(()->work1())
            .thenAccept(op-> System.out.println(op)).join();

System.err.println("Application ended");

输出:

ForkJoinPool.commonPool-worker-3
Application started
work1 called
12
Application ended

或者您可以在完成工作后让主线程等待

  System.err.println("Application started");

  CompletableFuture<Void> completableFuture = CompletableFuture
            .supplyAsync(()->work1())
            .thenAccept(op-> System.out.println(op));

  System.err.println("Application ended");

  completableFuture.join();

输出:

ForkJoinPool.commonPool-worker-3
Application started
Application ended
work1 called
25

如果您有多个 CompletableFuture 对象,那么您可以使用allOf 等待所有任务完成(但在后台每个可完成的任务都将异步执行)

CompletableFuture.allOf(completableFuture1,completableFuture1).join();

【讨论】:

  • CompletableFuture 通常用于异步任务执行。假设我有 4 个任务分配给 ForkJoinPool,而其他一些任务正在主线程上运行。 join() 会阻塞主线程。目的将失败。任何替代方法来解决这个问题?
【解决方案2】:

通过提供我自己的 Executor 实例,我实现了异步操作并避免将其标记为守护进程。 (任何类型的 Executor)

CompletableFuture
            .supplyAsync(()->work1(), Executors.newFixedThreadPool(2))
            .thenAccept(op-> System.out.println(op));

我认为这样可以避免创建守护线程,类似于我们在 ExecutorServices 中的做法。

感谢@Slaw 提供有关守护线程的信息。我想了解更多为什么 ForkJoin 架构会默认将线程标记为守护进程。

【讨论】:

  • 但是如果你真的需要它,我相信你可以使用返回守护线程的自定义线程工厂创建自己的ForkJoinPool。公共池不能这样做,因为否则开发人员将不得不手动关闭公共池(这对用户不友好)。当然,你不能关闭公共池。
  • 更正:“...返回非守护进程线程的自定义线程工厂”。
猜你喜欢
  • 1970-01-01
  • 2014-06-07
  • 1970-01-01
  • 2020-08-05
  • 2015-08-09
  • 2011-03-28
  • 2019-11-29
  • 1970-01-01
  • 2012-08-08
相关资源
最近更新 更多