现象
public static void main(String[] args) throws InterruptedException {
CompletableFuture.runAsync(() -> {
try {
Thread.sleep(1000);
System.out.println("ddd");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
System.out.println("ccc");
}
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
try {
Thread.sleep(1000);
System.out.println("ddd");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}).start();
System.out.println("ccc");
// Thread.sleep(2);
}
大家想想以上2段代码的输出都是什么呢?是一样的么?
结果,
代码1,输出:
ccc
代码2,输出:
ccc
ddd
科学吗?不应该一样吗?
原因
new Thread
当JVM启动时,通常有一个非守护的线程(它通常调用某个指定类的main方法)。JVM 继续执行线程,直到发生以下任何一种情况时停止:
- Runtime 类的 exit 方法已被调用,且安全管理器已允许执行退出操作(比如调用 Thread.interrupt 方法)
- 不是守护线程的所有线程都已死亡,要么从对 run 方法的调用返回,要么抛出一个在 run 方法之外传播的异常
new Thread是非守护线程,所以要等new Thread执行完成,JVM才会停止。
completableFuture
completableFuture是守护线程。
那么为什么内部创建的线程completableFuture是守护线程呢?
因为CompletableFuture.supplyAsync是由内部管理的ForkJoinPool。默认行为ForkJoinPool是创建守护线程。
对于守护线程,除非join()方法被调用,否则主线程永远不会等待它们完成,所以以上例子中,主线程先结束,其他线程仍在运行,但不会在控制台上打印任何内容。
可以使用以下方法控制线程各自的行为:
public final boolean isDaemon()
public final void setDaemon(boolean on)
所以,才出现了以上2种不一样的结果。