特点:所有用户线程结束,JVM也就停止了。
特点:JVM的停止与否与守护线程无关。
绝大多数应用开发场景都会使用用户线程,平时所说的线程也都指用户线程。
由于守护线程的特点,有些特别是中间件开发可以考虑使用守护线程,比如心跳检测,而JVM中的GC垃圾回收也是守护线程的典型应用。
也就是说如果做偏中间件的开发,可以考虑你所开发的服务是否应该影响JVM停止,如果不需要则可以使用守护线程,但如涉及到了资源关闭,那依旧需要使用用户线程。因为JVM停止不会考虑守护线程,所以守护线程中哪怕使用了finally也不一定会执行。
@Slf4j
public class DaemonAndUserThread {
/**
* 守护线程和用户线程的区别:
* 用户线程没都停止,虚拟机也不会停止.
* 用户线程都停止了,虚拟机会直接停止,虚拟机停止与守护线程无关.
* 守护线程应用场景:例如 GC垃圾回收,心跳检测.
*/
public static void main(String[] args) throws InterruptedException {
log.info("{} 主线程开始", Thread.currentThread());
// 主线程停止钩子函数(回调)
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
log.info("{} JVM停止", Thread.currentThread());
}));
// 守护线程 循环执行
AtomicInteger fre = new AtomicInteger(1);
Thread daemonThread = new Thread(() -> {
while (true) {
try {
log.info("{} 守护线程 开始第{}次调度", Thread.currentThread(), fre);
Thread.sleep(2000);
} catch (Exception e) {
log.info("{} 守护线程 第{}次调度发生异常", Thread.currentThread(), fre);
} finally {
log.info("{} 守护线程 第{}次执行finally", Thread.currentThread(), fre);
}
fre.getAndIncrement();
}
});
// 声明守护线程
daemonThread.setDaemon(true);
daemonThread.start();
// 用户线程 固定次数执行
Thread userThread = new Thread(() -> {
for (int i = 1; i <= 2 ; i++) {
try {
log.info("{} 用户线程 开始第{}次调度", Thread.currentThread(), i);
Thread.sleep(2000);
} catch (Exception e) {
log.info("{} 用户线程 第{}次调度发生异常", Thread.currentThread(), i);
} finally {
log.info("{} 用户线程 第{}次执行finally", Thread.currentThread(), i);
}
}
});
userThread.start();
Thread.sleep(3000);
log.info("{} 主线程唤醒", Thread.currentThread());
}
}
打印结果:
21:43:02.570 [main] INFO com.example.base.jdk.DaemonAndUserThread - Thread[main,5,main] 主线程开始
21:43:02.573 [Thread-1] INFO com.example.base.jdk.DaemonAndUserThread - Thread[Thread-1,5,main] 守护线程 开始第1次调度
21:43:02.574 [Thread-2] INFO com.example.base.jdk.DaemonAndUserThread - Thread[Thread-2,5,main] 用户线程 开始第1次调度
21:43:04.577 [Thread-1] INFO com.example.base.jdk.DaemonAndUserThread - Thread[Thread-1,5,main] 守护线程 第1次执行finally
21:43:04.577 [Thread-1] INFO com.example.base.jdk.DaemonAndUserThread - Thread[Thread-1,5,main] 守护线程 开始第2次调度
21:43:04.578 [Thread-2] INFO com.example.base.jdk.DaemonAndUserThread - Thread[Thread-2,5,main] 用户线程 第1次执行finally
21:43:04.578 [Thread-2] INFO com.example.base.jdk.DaemonAndUserThread - Thread[Thread-2,5,main] 用户线程 开始第2次调度
21:43:05.579 [main] INFO com.example.base.jdk.DaemonAndUserThread - Thread[main,5,main] 主线程唤醒
21:43:06.582 [Thread-1] INFO com.example.base.jdk.DaemonAndUserThread - Thread[Thread-1,5,main] 守护线程 第2次执行finally
21:43:06.583 [Thread-1] INFO com.example.base.jdk.DaemonAndUserThread - Thread[Thread-1,5,main] 守护线程 开始第3次调度
21:43:06.582 [Thread-2] INFO com.example.base.jdk.DaemonAndUserThread - Thread[Thread-2,5,main] 用户线程 第2次执行finally
21:43:06.585 [Thread-0] INFO com.example.base.jdk.DaemonAndUserThread - Thread[Thread-0,5,main] JVM停止
可以看到最终用户线程停止后JVM也就直接停止了,而守护线程也没能执行最后的finally,具体应该使用用户线程还是守护线程,需要根据应用场景灵活选择。