我麻了,京东一面:守护线程如何实现的?

守护线程,说白了,就是JVM运行后终止的线程。
用户线程和守护线程是 Java 的类型。
如果使用 setDaemon(true) 将线程转换为守护线程,则必须在 start() 之前执行此操作。

停止JVM的条件非常简单。
当用户线程执行完毕且数量归零时,守护线程被忽略并直接退出。
守护线程可能仍在运行,但 JVM 并不关心,只是将其停止。

守护线程无法随意访问文件、数据库和持久资源。
为什么?由于JVM可能会突然终止,导致资源无法正常释放,数据可能会变得混乱。

查看示例。
当主线程完成执行时,守护线程停止并且不再打印进一步的信息。
这就是 JVM 杀死它的方法。

JVM如何管理守护线程?有一个名为 _number_of_non_daemon_threads 的计数器。
这个变量可以在thread.cpp源代码中找到。
随着用户线程的添加或删除,该计数器也会相应地发生变化。
例如,计数器在remove()方法中更新。

JVM退出过程如下:main()方法执行完成后,调用LEAVE()→DestroyJavaVM()→destroy_vm()。
destroy_vm() 挂起。
当计数器返回到零时,意味着所有用户线程都已完成执行,JVM 将退出。
此时守护线程不再关心,将直接终止。
不执行最终性或异常处理。

守护线程创建的子线程默认也是守护线程,不需要手动设置。
例如,GC线程就是一个守护线程。
当程序结束时,它会在您不注意的情况下退出。

关键代码路径:当主线程退出时,java.c中会触发JavaMain()→LEAVE()→DestroyJavaVM()→destroy_vm()。
至于计数器管理,Threads::create_vm() 在添加线程时初始化计数器。
当线程被删除时,Thread::remove() 会递减计数器。
当计数器达到零时,destroy_vm() 被激活。

这样的设计主要是为了资源优化,避免程序结束后留下无用的线程。
例如GC线程在程序结束时运行,不占用任何资源。
但这里也存在风险。
守护线程将被强制退休,这可能会导致资源无法释放。
因此,开发人员必须小心处理,不要在守护线程中持有任何锁或文件句柄。

适用场景有后台日志记录、心跳检测、监控任务以及不重要​​的事情。
不合适的场景包括必须在退出之前完成的文件写入和数据库操作。
如果JVM突然停止,数据肯定会乱掉。

总之,守护线程取决于用户线程能否运行。
当 JVM 执行完毕后,它会退出并忽略守护线程。
该设计是为了平衡资源共享和程序鲁棒性。
适合辅助任务,但关键操作不能放在守护线程中。

Java中守护线程和用户线程的区别是什么?

用户线程参与业务逻辑。
守护线程支持后台任务。
所有用户线程完成后,JVM 退出。
当 JVM 退出时,守护进程启动结束。
用户启动必须提供终止。
守护线程可以被终止。
守护线程不使用关键资源。
线程类型在启动前设置。
本质的区别在于JVM生命周期的影响。

jstack的使用