上一篇文章里,我们知道了,当线程对象被创建时如果不指定线程组,构造器会默认指定当前线程所在的线程组作为新线程的线程组,在线程组的层面上,子线程和父线程实际上是平级的,并没有实际上的区别。
守护线程
今天我们来看看守护线程。守护线程本身很简单,只要在线程start方法执行之前,为线程对象设置setDaemon(true),就可以将当前线程设置为守护线程。如果在线程执行之后再设置,将会抛出异常。
下面是相关源代码:
/**
* 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;
}
可以看到,在为线程设置守护线程状态的时候,会先检查线程是否是存活状态(alive,如线程还未执行,则是Runnable),如果已经在运行了,则会抛出 IllegalThreadStateException 异常。
注意看注释,注释中说明了,当JVM中运行的所有线程全部都是守护线程的时候,JVM将会直接结束,反过来说,当只要还有一个用户线程存在,守护线程就会继续运行直到跑完,否则不管守护线程有没有执行完毕,都会直接结束。
但一开始,很多人也会有如下疑问:守护线程是否和创建它的父线程有关系?毕竟在逻辑上,在线程里创建线程,前者叫父线程,后者叫子线程,如果子线程是守护线程的话,是不是父线程结束了,子线程也跟着结束了呢?
实际上Java中的线程是不存在父子关系的,就如注释里说的那样,守护线程退出只有有两种情况:
- 线程本身执行完了
- JVM中没有用户线程了(非守护线程)
在线程中创建线程,除了会从“父线程”身上继承一些属性以外(比如线程组,守护线程标识,线程优先级等属性),两者在执行上是完全没有关系的。
下面是Thread的init方法中的片段:
Thread parent = currentThread();//在创建线程的时候,以当前线程为parent
SecurityManager security = System.getSecurityManager();
if (g == null) {
//....
if (g == null) {
g = parent.getThreadGroup();//parent的线程组
}
}
//....
g.addUnstarted();
this.group = g;
this.daemon = parent.isDaemon();//parent的守护线程标识
this.priority = parent.getPriority();//parent的优先级
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();//parent的上下文类加载器
else
this.contextClassLoader = parent.contextClassLoader;
//.....
因此,无论在哪里创建线程,虽然逻辑上我们认为他们是父子关系,但还是要记住,他们只是继承了一些属性而已,并不会互相影响,并不存在实际上的父子关系。
下面随便写了一个测试用的程序(很容易证明,所以不用看下面这一堆的==):
package fun.lain.laintest.thread;
public class DaemonThreadTest {
public static void main(String[] args) {
Thread1 thread1 = new Thread1();
thread1.start();
}
}
class Thread1 extends Thread{
@Override
public void run() {
Thread2 thread2 = new Thread2();
thread2.start();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Thread2 extends Thread{
@Override
public void run() {
Thread3 thread3 = new Thread3();
thread3.setDaemon(true);
thread3.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("T2 Priority:" + this.getPriority());
System.out.println("3s pass,T2 is end");
}
}
class Thread3 extends Thread{
@Override
public void run() {
try {
System.out.println("T3 is running");
System.out.println("T3 Priority:" + this.getPriority());
Thread.sleep(5_000);
System.out.println(this.isDaemon());
System.out.println(Thread.currentThread().getThreadGroup().getName());
Thread4 thread4 = new Thread4();
thread4.start();
Thread.sleep(1_500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("5s pass, T3 is end");
}
}
class Thread4 extends Thread{
@Override
public void run() {
try {
System.out.println("T4 is running");
Thread.sleep(1_000);
System.out.println("T4:" + this.isDaemon());
System.out.println(Thread.currentThread().getThreadGroup().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("1s pass, T4 is end");
}
}
运行结果:
T3 is running
T3 Priority:5
T2 Priority:5
3s pass,T2 is end
true
main
T4 is running
T4:true
main
1s pass, T4 is end
5s pass, T3 is end // T3是在T2中创建的守护线程,执行时间比T2长,在T2结束后依旧正常执行完毕,因此两者没有父子关系