关于CompletableFuture的使用以及细节这里不再介绍,主要研究一下CompletableFuture的异步执行流程,如果没有指定线程池,直接使用默认的ForkJoinPool.commonPool() 作为它的线程池执行异步代码,此线程池里的线程都是守护线程。这就会导致主线程完毕之后,CompletableFuture的执行也就结束了,因为用户线程结束后,守护线程也会结束,JVM会退出。
show me code,no bb。直接上代码:
public class CompletableFutureDemo5 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// ExecutorService threadPool1 = new ThreadPoolExecutor(1,
// 1,
// 5,
// TimeUnit.SECONDS,
// new LinkedBlockingQueue<>(5),
// Executors.defaultThreadFactory(),
// new ThreadPoolExecutor.AbortPolicy()
// );
try {
//当一个线程依赖另一个线程时用 thenApply 方法来把这两个线程串行化,
CompletableFuture.supplyAsync(() -> {
//暂停几秒钟线程
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "-->1024");
return 1024;
}).thenApply(f -> {
System.out.println(Thread.currentThread().getName() + "-->1025");
return f + 1;
}).thenApply(f -> {
int age = 10 / 0; // 异常情况:那步出错就停在那步。
System.out.println(Thread.currentThread().getName() + f);
return f + 1;
}).whenCompleteAsync((v, e) -> {
System.out.println(Thread.currentThread().getName() + "*****v: " + v);
}).exceptionally(e -> {
System.out.println(Thread.currentThread().getName() + "...................................");
e.printStackTrace();
System.out.println("zzzzzzzzzzzzzz");
return null;
});
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
// threadPool1.shutdown();
}
System.out.println(Thread.currentThread().getName() + "-----主线程结束,END");
}
}
可以看到打印结果:
main-----主线程结束,END
子线程并没有输出,因为主线程结束后,守护线程也会结束,JVM退出。
可以为CompletableFuture传入自定义线程池参数解决:
public class CompletableFutureDemo5 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService threadPool1 = new ThreadPoolExecutor(1,
1,
5,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(5),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
try {
//当一个线程依赖另一个线程时用 thenApply 方法来把这两个线程串行化,
CompletableFuture.supplyAsync(() -> {
//暂停几秒钟线程
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "-->1024");
return 1024;
}, threadPool1).thenApply(f -> {
System.out.println(Thread.currentThread().getName() + "-->1025");
return f + 1;
}).thenApply(f -> {
int age = 10 / 0; // 异常情况:那步出错就停在那步。
System.out.println(Thread.currentThread().getName() + f);
return f + 1;
}).whenCompleteAsync((v, e) -> {
System.out.println(Thread.currentThread().getName() + "*****v: " + v);
}).exceptionally(e -> {
System.out.println(Thread.currentThread().getName() + "...................................");
e.printStackTrace();
System.out.println("zzzzzzzzzzzzzz");
return null;
});
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
threadPool1.shutdown();
}
System.out.println(Thread.currentThread().getName() + "-----主线程结束,END");
}
}
打印结果:
main-----主线程结束,END
pool-1-thread-1–>1024
pool-1-thread-1–>1025
ForkJoinPool.commonPool-worker-9*****v: null
成功解决CompletableFuture提前退出问题,但这里有个疑问,回调函数
whenCompleteAsync和exceptionally采用的还是默认的ForkJoinPool.commonPool() 中守护线程,这里有个疑问,希望大佬可以指教一下。找到问题原因了,whenComplete和whenCompleteAsync是有区别的,whenComplete是执行当前任务的线程继续执行whenComplete的任务;而whenCompleteAsync是把whenCompleteAsync这个任务继续提交给线程池来进行执行,所以上面的问题是因为线程池中的新的守护线程来执行whenCompleteAsync任务,导致一旦主线程完毕后守护线程退出whenCompleteAsync任务不在执行。