问题
CompletableFuture cf1 = CompletableFuture.supplyAsync(() -> {
System.out.println("enter into completableFuture()");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("start to out of completableFuture()");
return "a";
});
System.out.println("do something else");
cf1.thenApply(v -> v + " b").thenAcceptAsync(v ->
System.out.println(v)
);
System.out.println("finalize...");
//cannot get expected result, if this line was comment out.
//TimeUnit.SECONDS.sleep(10);
code as above.
when writing a example of using CompletableFuture in jdk8, I got confused.
I must add the last line
TimeUnit.SECONDS.sleep(10);
to get the expected result.
I would like to know whether the program has ended if I do not let its main thread to sleep. if not, why I cannot get the output?
really thanks for you time.
回答1:
A JVM terminates when no non-daemon threads are running, so if asynchronous operations are executed by daemon threads only, it will terminate when the main thread terminates, not continuing the background operations.
There are several ways to solve this.
If the background computations form a single dependency chain, you can use the last operation to wait for its completion, as its completion implies the completion of all previous stages. Letting the main thread wait until the completion defers the JVM’s termination to that point:
CompletableFuture cf1 = CompletableFuture.supplyAsync(() -> { System.out.println("enter into completableFuture()"); LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1)); System.out.println("start to out of completableFuture()"); return "a"; }); System.out.println("do something else"); CompletableFuture last = cf1.thenApply(v -> v + " b").thenAcceptAsync(System.out::println); System.out.println("finalize..."); last.join();
Consider the documentation of CompletableFuture:
All async methods without an explicit Executor argument are performed using the ForkJoinPool.commonPool() (unless it does not support a parallelism level of at least two, in which case, a new Thread is created to run each task).
Since it’s the property of that F/J common pool to use daemon threads, we can use that knowledge to wait for the completion of all pending tasks in that case, which works independently of the dependencies between these pending tasks:
CompletableFuture cf1 = CompletableFuture.supplyAsync(() -> { System.out.println("enter into completableFuture()"); LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1)); System.out.println("start to out of completableFuture()"); return "a"; }); System.out.println("do something else"); cf1.thenApply(v -> v + " b").thenAcceptAsync(System.out::println); System.out.println("finalize..."); if(ForkJoinPool.getCommonPoolParallelism()>1) ForkJoinPool.commonPool().awaitQuiescence(1, TimeUnit.DAYS);
Use an explicit executor, which will not use daemon threads. The thread pool executors provided by the JRE, letting
ForkJoinPool
aside, use non-daemon threads by default:final ExecutorService threadPool = Executors.newCachedThreadPool(); CompletableFuture cf1 = CompletableFuture.supplyAsync(() -> { System.out.println("enter into completableFuture()"); LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1)); System.out.println("start to out of completableFuture()"); return "a"; }, threadPool); System.out.println("do something else"); cf1.thenApply(v -> v + " b").thenAcceptAsync(System.out::println); System.out.println("finalize..."); threadPool.shutdown();
Note that
threadPool.shutdown();
does not imply waiting nor does it stop pending tasks; it only stops accepting new tasks and ensures that the pooled threads will terminate once all pending tasks have been processed. You could place it directly after using it withsupplyAsync
, without changing the behavior.So the third solution is the only one letting the
main
thread exit, with the JVM continuing until all pending background tasks are processed, as they are running in non-daemon threads.
回答2:
you can suspend a CompletableFuture
until completed by CompletableFuture#join, for example:
CompletableFuture<Void> stage = cf1.thenApply(v -> v + " b").thenAcceptAsync(v ->
System.out.println(v)
);
System.out.println("finalize...");
// v--- the main thread wait until the stage is completed
stage.join();
来源:https://stackoverflow.com/questions/44540862/instance-of-completablefuture-cannot-get-expected-result