Java里有一种特殊的线程叫做守护(daemon)线程。这种线程的优先级很低,通常来说,当同一个应用程序里没有其它的线程运行的时候,守护线程才运行。当守护线程是程序中唯一运行的线程时,守护线程执行结束后,JVM也就结束了这个程序。
因为这种特性,守护线程通常被用来做为同一程序中普通线程(也称为用户线程)的服务提示者。它们通常是无限循环的,以等待服务请求或执行线程的任务。它们不能做重要的工作,因为我们不可能知道守护线程什么时候能够获取CPU时钟,并且,在没有其他线程运行的时候,守护线程随时可能结束。一个典型的守护线程是java的垃圾回收器(Garbage Collector)。
范例程序包含两个线程:一个是用户线程,它将事件写入到一个队列中;另一个是守护线程,它将管理这个队列,如果生成的事件超过10秒钟,就会被移除。
package concurrency;import java.util.Date;import java.util.concurrent.LinkedBlockingDeque;import java.util.concurrent.TimeUnit;public class Main1 { public static void main(String[] args) { LinkedBlockingDequedeque = new LinkedBlockingDeque (); WriterTask writer = new WriterTask(deque); for(int i = 0;i < 3; i++){ Thread thread = new Thread(writer); thread.start(); } CleanerTask cleaner = new CleanerTask(deque); cleaner.start(); }}class Event{ private Date date; private String event; public Date getDate() { return date; } public void setDate(Date date) { this.date = date; } public String getEvent() { return event; } public void setEvent(String event) { this.event = event; }}class WriterTask implements Runnable{ private LinkedBlockingDeque deque; public WriterTask(LinkedBlockingDeque deque){ this.deque = deque; } public void run() { for(int i = 1; i <= 20; i++){ Event event = new Event(); event.setDate(new Date()); event.setEvent(String.format("The thread %s has generated an event", Thread.currentThread().getId())); deque.addFirst(event); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } }}class CleanerTask extends Thread{ private LinkedBlockingDeque deque; public CleanerTask(LinkedBlockingDeque deque){ this.deque = deque; this.setDaemon(true); } @Override public void run() { while(true){ Date date = new Date(); clean(date); } } private void clean(Date date){ long difference; boolean delete; if(deque.size() == 0) return; delete = false; do { Event e = deque.getLast(); difference = date.getTime() - e.getDate().getTime(); if(difference > 10000){ System.out.printf("Cleaner: %s\n", e.getEvent()); deque.removeLast(); delete = true; } } while (difference > 10000); if(delete){ System.out.printf("Cleaner: Size of the queue : %d\n", deque.size()); } }}
对程序的运行输出进行分析之后,我们会发现,队列中的对象会不断增长直到30个,然后到程序结束,队列的长度维持在27~30之间。
这个程序有3个WriterTask线程,每个线程向队列写入一个事件,然后休眠1秒钟。在第一个10秒钟内,队列中有30个事件,直到3个WriterTask都休眠后,CleanerTask才开始执行,但是它没有删除任何事件。因为所有的事件都小于10秒钟。在接下来的运行中,CleanerTask每秒删除3个对象,同时WriterTask会写入3个对象,所以队列的长度一直介于27~30之间。
setDaemon()方法只能在start()方法被调用之前设置。一旦线程开始运行,将不能再修改守护状态。isDaemon()方法被用来检查一个线程是不是守护线程,返回值true,表示这个线程是守护线程; false表示这个线程是用户线程。
注意:1. thread.setDaemon(true)必须在thread.start()之前设置。2. 在Daemon线程中产生的新线程也是Daemon的。新创建一个新Thread对象时,该新线程的初始优先级被设定为创建线程的优先级,并且当且仅当创建线程是守护线程时,新线程才是守护线程。3. 当用户线程(非守护线程)全部结束,则守护线程也随即结束,否则守护线程将一直执行(不管你的守护线程业务逻辑是不是while(true)这样的死循环)。