两种线程
一般情况下,不做特殊说明配置,默认是用户线程
用户线程(User Thread)
是系统工作线程,它会完成这个程序需要完成的业务操作。
守护线程(Deamon Thread)
是一种比较特殊的线程为其他的线程服务,在后台默认的完成一些系统服务,比如垃圾回收线程就是一个典型的例子。
守护线程作为一个服务线程,没有服务对象就没有必要继续运行了,如果守护线程全部结束了,意味着程序需要完成的任务已经结束了,系统就可以退出了。所以假如系统只剩下守护线程的时候,Java 虚拟机会自动退出。
设置守护线程
线程的 daemon 属性
- Thread 的 daemon 默认值为 false 表示是用户线程
![](https://file.jishuzhan.net/article/1695973284111192066/78798908ca254953940ef718cef4ce33.webp)
- 设置守护线程后,如果用户线程执行完毕后,JVM 进程退出。例子如下:
java
package io.pipi;
import java.util.concurrent.TimeUnit;
public class Main {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t 开始初始化"
+ (Thread.currentThread().isDaemon() ? "守护线程" : "用户线程"));
while (true) {
}
}, "t1");
t1.setDaemon(true);
t1.start();
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + "\t ---end 主线程");
}
}
如果设置 t1.setDaemon(true); 后台线程,当线程执行完毕过后退出
JVM 源码
- jvm 中使用 Threads 类跟踪正在运行的线程
cpp
class Threads: AllStatic {
friend class VMStructs;
private:
static int _number_of_threads; //系统中正在运行的线程
static int _number_of_non_daemon_threads; //非守护线程数
public:
static void destroy_vm(); //main 方法结束后回调
}
- Threads::destroy_vm 方法会在 main 方法结束后调用
cpp
void Threads::destroy_vm() {
JavaThread* thread = JavaThread::current();
// Wait until we are the last non-daemon thread to execute, or
// if we are a daemon then wait until the last non-daemon thread has
// executed.
bool daemon = java_lang_Thread::is_daemon(thread->threadObj());
int expected = daemon ? 0 : 1;
//等待最后一个非守护线程退出
{
MonitorLocker nu(Threads_lock);
while (Threads::number_of_non_daemon_threads() > expected)
// This wait should make safepoint checks, wait without a timeout.
nu.wait(0);
}
// 执行 shutdown hook
// run Java level shutdown hooks
thread->invoke_shutdown_hooks();
// 其他资源释放操作
}
所以我们从 jvm 源码中可以看到,守护线程不会影响 jvm 的退出,也验证Thread.java 中的描述。 机器翻译如下(更加方便佐证结论):
![](https://file.jishuzhan.net/article/1695973284111192066/a7b20f7f62484e2e9e023b4c9790beaa.webp)
总结
- 如果用户线程全部结束意味着程序需要完成的任务已经结束了,守护线程随着 JVM 一同工作
- setDaemon(true) 必须在 start() 之前设置,否则会报
java.lang.IllegalThreadStateException
异常
![](https://file.jishuzhan.net/article/1695973284111192066/90d4429c0a084948be30b2a520559cc5.webp)