(下面的描述和代码摘抄自书目《java编程思想》)
所谓后台(daemon)线程,是指在程序运行的时候在后台提供一种通用服务的线程,并且这种线程并不属于程序中不可或缺的部分。因此,当所有非后台线程结束时,程序也就终止了,同时会杀死所有的后台线程。反过来说,只要有任何非后台线程还在运行,程序就不会终止。比如,执行main()的就是一个非后台线程。
1.设置后台线程的方法是在线程启动之前调用setDaemon()方法
public class SimpleDaemons implements Runnable{
public void run(){
try {
while(true){
TimeUnit.MILLISECONDS.sleep(100);
System.out.println(Thread.currentThread() +" "+this);
}
} catch (InterruptedException e) {
System.out.println("sleep() interrupted");
e.printStackTrace();
}
}
public static void main(String[] args)throws Exception{
for (int i = 0; i < 10; i++) {
Thread daemon = new Thread(new SimpleDaemons());
daemon.setDaemon(true);//Must call before start()
daemon.start();
}
System.out.println("All daemons started");
TimeUnit.MILLISECONDS.sleep(500);//main()此时为非后台线程,其他为后台线程,所以当main()结束工作时,整个程序终止,可以修改休眠时间来观察不同的结果
}
}
2.上述代码中的SimpleDaemons.java创建了显示的线程,以便可以设置后台标志。可以通过编写定制的ThreadFactory,有Executor创建定制的线程。代码如下:
/**1.通过实现ThreadFactory将线程全部设置为后台线程
* */
public class DaemonThreadFactory implements ThreadFactory{
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setDaemon(true);
return t;
}
}
/**2.创建线程
* */
public class DaemonFromFactory implements Runnable{
public void run(){
try {
while(true){
TimeUnit.MILLISECONDS.sleep(100);
System.out.println(Thread.currentThread() +" "+this);
}
} catch (InterruptedException e) {
System.out.println("Interrupted");
}
}
public static void main(String[] args) throws Exception{
ExecutorService exec = Executors.newCachedThreadPool(new DaemonThreadFactory());
for (int i = 0; i < 10; i++) {
exec.execute(new DaemonFromFactory());
}
System.out.println("All daemons started");
TimeUnit.MILLISECONDS.sleep(100);//Run for a while
}
}
这和普通的ThreadFactory的唯一区别就是它将线程的后台状态全部设置了true.
3.当一个线程被设置成了后台模式,它所派生的所有子线程全部是后台线程。代码如下(可以运行下面代码查看结果):
public class Daemon implements Runnable{
private Thread[] t = new Thread[10];
public void run(){
for (int i = 0; i < t.length; i++) {
t[i] = new Thread(new DaemonSpawn());
t[i].start();
System.out.println("DaemonSpawn "+i+" started, ");
}
for (int i = 0; i < t.length; i++)
System.out.println("t["+i+"].isDaemon()= "+t[i].isDaemon()+", ");
while(true) Thread.yield();
}
}
public class DaemonSpawn implements Runnable {
public void run(){
while(true){
Thread.yield();
}
}
}
public class Daemons {
public static void main(String[] args)throws Exception{
Thread d = new Thread(new Daemon());
d.setDaemon(true);
d.start();
System.out.println("d.isDaemon()= "+d.isDaemon()+", ");
//Allow the Daemon threads to finish their startup process
TimeUnit.MILLISECONDS.sleep(10);
}
}
运行结果为run()中产生的10个子线程均为后台线程,此处就不贴运行结果了。
4.后台进程在不执行finally子句的情况下就可能会终止run()方法。代码如下:
public class ADeamon implements Runnable {
public void run(){
try {
System.out.println("starting ADeamon");
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
System.out.println("Exiting via InterruptedException");
}finally{
System.out.println("This should always run?");
}
}
public static void main(String[] args) throws InterruptedException{
Thread t= new Thread(new ADeamon());
t.setDaemon(true);
t.start();
TimeUnit.MILLISECONDS.sleep(20);
}
}
运行结果只打印了:starting ADeamon
但是注释掉setDaemon()的调用,就会看到finally子句执行。
这种行为是正确的,因为当最后一个非后台线程终止时,后台线程会“突然”终止,因此一旦main()退出,JVM就会立即关闭后台线程。