Java分为两种线程:用户线程和守护线程;

所谓守护线程是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。因 此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。

守护线程和用户线程的没啥本质的区别:唯一的不同之处就在于虚拟机的离开:如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了。 因为没有了被守护者,守护线程也就没有工作可做了,也就没有继续运行程序的必要了。

将线程转换为守护线程可以通过调用Thread对象的setDaemon(true)方法来实现。在使用守护线程时需要注意一下几点:

(1) thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。

(2) 在Daemon线程中产生的新线程也是Daemon的。

(3) 守护线程应该永远不去访问固有资源,如文件、数据库,因为它会在任何时候甚至在一个操作的中间发生中断。

(4) 写java多线程程序时,一般比较喜欢用java自带的多线程框架,比如ExecutorService,但是java的线程池会将守护线程转换为用户线程,所以如果要使用后台线程就不能用java的线程池。

如下,线程池中将daemon线程转换为用户线程的程序片段:

class DefaultThreadFactory implements ThreadFactory {
	private static final AtomicInteger poolNumber = new AtomicInteger(1);
	private final ThreadGroup group;
	private final AtomicInteger threadNumber = new AtomicInteger(1);
	private final String namePrefix;

	DefaultThreadFactory() {
		SecurityManager s = System.getSecurityManager();
		group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
		namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-";
	}

	public Thread newThread(Runnable r) {
		Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
		if (t.isDaemon())
			t.setDaemon(false);
		if (t.getPriority() != Thread.NORM_PRIORITY)
			t.setPriority(Thread.NORM_PRIORITY);
		return t;
	}
}

注意到,这里不仅会将守护线程转变为用户线程,而且会把优先级转变为Thread.NORM_PRIORITY。
如下所示,将守护线程采用线程池的方式开启:

class DaemonThreadTest  
{  
    public static void main(String[] args)  
    {  
        Thread mainThread = new Thread(new Runnable(){  
            @Override 
            public void run()  
            {  
                ExecutorService exec = Executors.newCachedThreadPool();  
                Thread childThread = new Thread(new ClildThread());  
                childThread.setDaemon(true);  
                exec.execute(childThread);  
                exec.shutdown();  
                System.out.println("I'm main thread...");  
            }  
        });  
        mainThread.start();  
    }  
}  
   
class ClildThread implements Runnable  
{  
    @Override 
    public void run()  
    {  
        while(true)  
        {  
            System.out.println("I'm child thread..");  
            try 
            {  
                TimeUnit.MILLISECONDS.sleep(1000);  
            }  
            catch (InterruptedException e)  
            {  
                e.printStackTrace();  
            }  
        }  
    }  
}

本博文实例代码:

public class MutilThreadDemo_05 {
	public static void main(String[] args) throws InterruptedException {
		TrainTicket5 test1 = new TrainTicket5();
		Thread thread = new Thread(test1, "子线程");
		/** 1、当子线程为非守护线程时,子线程的死循环代码会一直输出;
		 *  2、当子线程设置为守护线程时,当主线程执行完毕,子线程也随之结束,不再输出;
		 * 注意:
		 * 	thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。
		 * 	你不能把正在运行的常规线程设置为守护线程。
		 */
		 thread.setDaemon(true); 
		thread.start();
		for (int i = 0; i < 10; i++) {
			Thread.sleep(100);
			System.out.println(Thread.currentThread().getName() + " : " + i);
		}
	}
}

class TrainTicket5 implements Runnable {

	@Override
	public void run() {
		for (int i = 0; i < 100; i++) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
			}
			System.out.println(Thread.currentThread().getName() + " : " + i);
			if (i == 99) {
				i = 0;
			}
		}
	}
}

结果展示:

1:当子线程设置为守护线程时,当主线程执行完毕,子线程也随之结束,不再输出

thread.setDaemon(true); 

2、当子线程为非守护线程时,子线程的死循环代码会一直输出

thread.setDaemon(false); 

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐