本文介绍了CompletableFuture没有得到执行.如果我使用ExecutorService池,则其工作正常,但不使用默认的forkJoin公共池的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图运行以下类,使其终止而不执行CompletableFuture.

public class ThenApplyExample {

public static void main(String[] args) throws Exception {
    //ExecutorService es = Executors.newCachedThreadPool();
    CompletableFuture<Student> studentCompletableFuture = CompletableFuture.supplyAsync(() -> {

        try {

            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return 3;
    })// If I put executorservice created n commented above, programme work as expected.
            .thenApply(i -> {

                for (int j = 0; j <= i; j++) {
                    System.out.println("Inside first then apply");
                }
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("First then apply is finished");
                return ++i;
            })
            .thenApply(i -> {
                System.out.println("Inside 2nd then apply");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Inside 2nd then apply stopped");

                return i++;
            })
            .thenApply(i -> {
                System.out.println("Inside 3nd then apply");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Inside 3nd then apply stopped");
                return "The i is ::: " + i;
            })
            .thenApply(s -> Student.builder().id(1).name(s).address("Some address").build());
    System.out.println("Executing..");
    System.out.println("Executing..");
    System.out.println("Executing..");
    System.out.println("Executing..");
    System.out.println("Executing..");

    //es.shutdown();
}
}

我得到的输出是

Executing..
Executing..
Executing..
Executing..
Executing..

预期输出为

Executing..
Executing..
Executing..
Executing..
Executing..
Inside first then apply
Inside first then apply
Inside first then apply
Inside first then apply
First then apply is finished
Inside 2nd then apply
Inside 2nd then apply stopped
Inside 3nd then apply
Inside 3nd then apply stopped

如果我在程序的最后添加了 studentCompletableFuture.get(),它会按预期运行,或者如果我在supplyAsync第二个参数中添加了 executorservice (请检查在节目中发表评论),它会再次按预期运行.

我的问题是,当程序使用默认的ForkJoin公共池时,为什么它会终止?

解决方案

ForkJoinPool daemon 线程(至少默认情况下).这是相关的Javadoc(强调我的):

Executors创建的ExecutorService中的线程是非守护程序线程.我相信,例外是Executors.newWorkStealingPool.我找不到关于此的文档,但是至少这是当前如何实现的文档.您可以通过提供自定义ThreadFactory来更改此设置.

守护进程线程不能按照 Thread 的Javadoc(强调我的):

在您的代码中,您具有以下内容(简体):

public static void main(String[] args) {
    CompletableFuture<Student> future = CompletableFuture.supplyAsync(/* Supplier */)
            .andThen(/* Function One */)
            .andThen(/* Function Two */)
            .andThen(/* Function Three */)
            .andThen(/* Final Function */);
}

这使用了常见的ForkJoinPool,如上所述,它使用了守护程序线程.异步代码是从主线程启动的,但是您不必等待它完成.这意味着主线程退出,因此JVM也存在.这是在您的异步代码有机会完成之前发生的.

然后您尝试了呼叫get():

public static void main(String[] args) throws Exception {
    Student student = CompletableFuture.supplyAsync(/* Supplier */)
            .andThen(/* Function One */)
            .andThen(/* Function Two */)
            .andThen(/* Function Three */)
            .andThen(/* Final Function */)
            .get();
}

这是有效的,因为get()是阻止呼叫;这意味着主线程现在要等到异步代码完成后才能继续.换句话说,您将使主线程保持活动状态,从而使JVM保持活动状态.

当您使用来自Executors.newCachedThreadPool()的自己的ExeuctorService时,执行线程是非守护程序.这意味着异步代码正在这些非守护进程线程上运行,这使JVM保持活动状态,直到所述代码完成为止.实际上,即使您不调用ExecutorService.shutdown(),异步代码完成后 也可以使JVM保持活动状态(尽管缓存的线程池可能允许所有线程在一定时间后死亡). /p>

在问题注释中,您询问是否存在一种更优雅"的方法来使主线程保持活动状态(get()除外).我不确定您对优雅"的定义是什么,但是有 CompletableFuture.join() 方法.它像get()一样等待将来(正常或异常)完成后再返回.但是,与get()不同,它不会引发检查异常. 但等待也不能中断.


您还声明了

我不知道为什么会这样.运行此代码:

public static void main(String[] args) {
    CompletableFuture.runAsync(() -> {
        Thread t = Thread.currentThread();
        System.out.printf("Thread_Name: %s, Daemon: %s%n", t.getName(), t.isDaemon());
    }).join();
}

给我这个:

 Thread_Name: ForkJoinPool.commonPool-worker-9, Daemon: true
 

I am trying to run the following class its getting terminated without executing the CompletableFuture.

public class ThenApplyExample {

public static void main(String[] args) throws Exception {
    //ExecutorService es = Executors.newCachedThreadPool();
    CompletableFuture<Student> studentCompletableFuture = CompletableFuture.supplyAsync(() -> {

        try {

            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return 3;
    })// If I put executorservice created n commented above, programme work as expected.
            .thenApply(i -> {

                for (int j = 0; j <= i; j++) {
                    System.out.println("Inside first then apply");
                }
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("First then apply is finished");
                return ++i;
            })
            .thenApply(i -> {
                System.out.println("Inside 2nd then apply");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Inside 2nd then apply stopped");

                return i++;
            })
            .thenApply(i -> {
                System.out.println("Inside 3nd then apply");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Inside 3nd then apply stopped");
                return "The i is ::: " + i;
            })
            .thenApply(s -> Student.builder().id(1).name(s).address("Some address").build());
    System.out.println("Executing..");
    System.out.println("Executing..");
    System.out.println("Executing..");
    System.out.println("Executing..");
    System.out.println("Executing..");

    //es.shutdown();
}
}

Output I am getting is

Executing..
Executing..
Executing..
Executing..
Executing..

Whereas expected output is

Executing..
Executing..
Executing..
Executing..
Executing..
Inside first then apply
Inside first then apply
Inside first then apply
Inside first then apply
First then apply is finished
Inside 2nd then apply
Inside 2nd then apply stopped
Inside 3nd then apply
Inside 3nd then apply stopped

If I add studentCompletableFuture.get() at the very end of the programme, it works as expected or if I add the executorservice in the supplyAsync 2nd argument(check comment in programme), it works again as expected.

My question is why it is terminating when programme uses the default ForkJoin common pool?

解决方案

The threads in the ForkJoinPool are daemon threads (at least by default). Here's the relevant Javadoc (emphasis mine):

The threads in the ExecutorService created by Executors are non-daemon threads. The exception, I believe, is Executors.newWorkStealingPool. I couldn't find documentation on this but this is how it's currently implemented at least. You can change this by supplying a custom ThreadFactory.

Daemon threads do not keep the JVM alive as stated in Thread's Javadoc (emphasis mine):

In your code you have the following (simplified):

public static void main(String[] args) {
    CompletableFuture<Student> future = CompletableFuture.supplyAsync(/* Supplier */)
            .andThen(/* Function One */)
            .andThen(/* Function Two */)
            .andThen(/* Function Three */)
            .andThen(/* Final Function */);
}

This uses the common ForkJoinPool which, as stated, uses daemon threads. The async code is launched from the main thread but you don't wait for it to complete. This means the main thread exits and therefore the JVM also exists. This happens before your async code had a chance to complete.

Then you tried a call to get():

public static void main(String[] args) throws Exception {
    Student student = CompletableFuture.supplyAsync(/* Supplier */)
            .andThen(/* Function One */)
            .andThen(/* Function Two */)
            .andThen(/* Function Three */)
            .andThen(/* Final Function */)
            .get();
}

This works because get() is a blocking call; meaning the main thread now waits until the async code completes before continuing. In other words, you are keeping the main thread alive which keeps the JVM alive.

When you use your own ExeuctorService from Executors.newCachedThreadPool() the executing threads are non-daemon. This means the async code is being run on these non-daemon threads which keeps the JVM alive until said code completes. In fact, it would keep the JVM alive even after the async code completes if you don't call ExecutorService.shutdown() (though a cached thread pool might allow all threads to die after a certain time).

In the question comments you ask if there is a more "elegant" way to keep the main thread alive (other than get()). I'm not sure what your definition of "elegant" is but there is the CompletableFuture.join() method. It waits, like get(), for the future to complete (normally or exceptionally) before it returns. Unlike get(), however, it doesn't throw checked exceptions; but the wait also cannot be interrupted.


You also state,

I don't know why that's the case. Running this code:

public static void main(String[] args) {
    CompletableFuture.runAsync(() -> {
        Thread t = Thread.currentThread();
        System.out.printf("Thread_Name: %s, Daemon: %s%n", t.getName(), t.isDaemon());
    }).join();
}

Gives me this:

Thread_Name: ForkJoinPool.commonPool-worker-9, Daemon: true

这篇关于CompletableFuture没有得到执行.如果我使用ExecutorService池,则其工作正常,但不使用默认的forkJoin公共池的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-22 20:26