用户线程与守护线程
大约 3 分钟
用户线程
一般情况下,我们启动一个线程,默认就是用户线程。它是系统的工作线程,程序的业务操作都由它来完成。
守护线程
守护线程是服务与其它线程的线程,例如垃圾回收线程就是一个守护线程。
守护线程作为一个服务线程,是不能独立存在的,必须要一个服务对象。还是用垃圾回收线程来说明,当 Java 程序在运行的过程中,必然有一个主线程也就是 main 方法执行的线程,当主线程还在执行的过程中,垃圾回收线程会一直运行;当程序运行结束,主线程结束,那么垃圾回收线程也会相应的退出。
源码说明
在 Java 中,在不特殊设置下,启动一个线程默认都是用户线程,我们可以通过配置 Thread 中的一个属性来决定即将启动的线程是用户线程还是守护线程。
/* Whether or not the thread is a daemon thread. */
private boolean daemon = false;
通过调用 setDaemon(boolean on)
进行配置。
/**
* Marks this thread as either a {@linkplain #isDaemon daemon} thread
* or a user thread. The Java Virtual Machine exits when the only
* threads running are all daemon threads.
*
* <p> This method must be invoked before the thread is started.
*
* @param on
* if {@code true}, marks this thread as a daemon thread
*
* @throws IllegalThreadStateException
* if this thread is {@linkplain #isAlive alive}
*
* @throws SecurityException
* if {@link #checkAccess} determines that the current
* thread cannot modify this thread
*/
public final void setDaemon(boolean on) {
// 检查是否有权限修改该线程
checkAccess();
// 如果当前线程已启动,那么抛出异常
if (isAlive()) {
throw new IllegalThreadStateException();
}
daemon = on;
}
值得注意的是,该方法必须在调用 start() 方法之前执行,否则会抛出异常。
通过 isDaemon()
方法来判断当前线程是否是守护线程。
/**
* Tests if this thread is a daemon thread.
*
* @return <code>true</code> if this thread is a daemon thread;
* <code>false</code> otherwise.
* @see #setDaemon(boolean)
*/
public final boolean isDaemon() {
return daemon;
}
如何启动一个守护线程?
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
Thread currentThread = Thread.currentThread();
System.out.printf("%s is daemon thread: %s\n", currentThread.getName(), currentThread.isDaemon());
while (true) {
// 模拟业务
}
}, "thread-daemon");
// daemon 默认为false,设置 true,该线程将成为守护线程
thread.setDaemon(true);
// 启动线程
thread.start();
// 主线程暂停 3 秒
TimeUnit.SECONDS.sleep(3);
System.out.printf("%s: over\n", Thread.currentThread().getName());
}
}
thread-daemon is daemon thread: true
main: over
在这个案例中,通过调用 setDaemon(boolean on)
将 thread 设置为守护线程;它会随着 main 方法执行结束而结束。如果不去调用这个方法,那么该程序会一直执行下去。
值得注意的是,守护线程创建的线程也守护线程。
修改代码
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
Thread currentThread = Thread.currentThread();
System.out.printf("%s is daemon thread: %s\n", currentThread.getName(), currentThread.isDaemon());
// 创建一个新线程
Thread t1 = new Thread(() -> {
Thread currentThread1 = Thread.currentThread();
System.out.printf("%s is daemon thread: %s\n", currentThread1.getName(), currentThread1.isDaemon());
while (true) {
// 模拟业务
}
}, "t1-daemon");
// 启动
t1.start();
while (true) {
// 模拟业务
}
}, "thread-daemon");
// daemon 默认为false,设置 true,该线程将成为守护线程
thread.setDaemon(true);
// 启动线程
thread.start();
// 主线程暂停 3 秒
TimeUnit.SECONDS.sleep(3);
System.out.printf("%s: over\n", Thread.currentThread().getName());
}
}
thread-daemon is daemon thread: true
t1-daemon is daemon thread: true
main: over
总结
用户线程是主要业务执行者,守护线程是服务于用户线程。守护线程不应该访问、写入持久化资源,如文件、数据库,因为它会在任何时间被停止,导致资源未释放、数据写入中断等问题。由守护线程创建的线程仍然是守护线程。