所谓后台(daemon)线程,是指在程序运行的时候再后台提供一种通用服务的线程,并且这种线程不属于程序中不可获取的部分。因此,当所有的非后台线程结束时,程序也就终止了,同时杀死进程中的所有后台线程。反过来说,只要有任何非后台线程还在运行,程序就不会终止。比如,执行main()的就是一个非后台线程。
1、非后台线程的生命周期
class LifeTest implements Runnable
{
@Override
public void run()
{
while(true)
{
System.out.println("running...");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class MultiThreadTest
{
public static void main(String[] args)
{
Thread t = new Thread(new LifeTest());
t.start();
}
}
在这个程序中,有两个线程,一个是main函数所在的线程,这个线程又启动了另外一个线程来执行LifeTest任务。此时的线程结构如下:
main函数所在的线程Thread [main]在调用完t.start启动另外一个线程后,就会退出。那么新的线程会因为main线程的退出而退出么?我们来看一下Thread [main]退出后的线程结构:
可以看到,Thread[main]线程退出之后,执行LiftTest任务的线程Thread[Thread-0]仍然在运行,它仍在每隔一秒打印一条running...
从上图可以看到,在Thread[main]退出之后,一个新线程Thread[DestroyJavaVM]又被唤醒了,它的作用是在所有非后台线程结束后卸载JVM,参见附录。
总结:如果没有显示的控制非后台线程的生命周期,如调用Thread.stop等,非后台线程会一直存在直到完成它的任务(run方法运行结束)。
2、后台线程的生命周期
我们把上面启动线程的那段代码修改一下,以后台线程的方式启动新线程
public static void main(String[] args)
{
Thread t = new Thread(new LifeTest());
//must be setted before start thread
t.setDaemon(true);
t.start();
}
再次用debug模式查看线程栈的时候,就会发现新线程随着Thread[main]的退出而退出了。
总结:当所有的非后台线程结束时,程序也就终止了,同时杀死进程中的所有后台线程。
附录
1、DestroyJavaVM线程
执行main()的线程在main执行完后调用JNI中的jni_DestroyJavaVM()方法唤起DestroyJavaVM线程。 JVM在Jboss服务器启动之后,就会唤起DestroyJavaVM线程,处于等待状态,等待其它线程(java线程和native线程)退出时通知它卸载JVM。线程退出时,都会判断自己当前是否是整个JVM中最后一个非deamon线程,如果是,则通知DestroyJavaVM线程卸载JVM。
ps:
扩展一下:
a.如果线程退出时判断自己不为最后一个非deamon线程,那么调用thread->exit(false),并在其中抛出thread_end事件,jvm不退出。
b.如果线程退出时判断自己为最后一个非deamon线程,那么调用before_exit()方法,抛出两个事件: 事件1:thread_end线程结束事件、事件2:VM的death事件。
然后调用thread->exit(true)方法,接下来把线程从active list卸下,删除线程等等一系列工作执行完成后,则通知正在等待的DestroyJavaVM线程执行卸载JVM操作。
参见链接http://code.google.com/p/hatter-source-code/wiki/Study_Java_HotSpot_Thread