介绍
用户线程即运行在前台的线程,而守护线程是运行在后台的线程。 守护线程作用是为其他前台线程的运行提供便利服务,比如垃圾回收线程就是一个守护线程。
其实User Thread线程和Daemon Thread守护线程本质上来说去没啥区别的,唯一的区别之处就在虚拟机的离开:如果User Thread全部撤离,那么Daemon Thread也就没啥线程好服务的了,所以Daemon Thread也就退出了。
简单的讲就是JVM的退出不用考虑守护线程的退出,而必须考虑用户线程的退出。
用户也可以自行的设定守护线程,方法:public final void setDaemon(boolean on)
;但是有几点需要注意:
- thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。 (备注:这点与守护进程有着明显的区别,守护进程是创建后,让进程摆脱原会话的控制+让进程摆脱原进程组的控制+让进程摆脱原控制终端的控制;所以说寄托于虚拟机的语言机制跟系统级语言有着本质上面的区别)
- 在Daemon线程中产生的新线程也是Daemon的。 (这一点又是有着本质的区别了:守护进程fork()出来的子进程不再是守护进程,尽管它把父进程的进程相关信息复制过去了,但是子进程的进程的父进程不是init进程,所谓的守护进程本质上说就是“父进程挂掉,init收养,然后文件0,1,2都是/dev/null,当前目录到/”)
- 守护线程不能用于去访问固有资源,比如读写操作或者计算逻辑。因为在Daemon Thread还没来的及进行操作时,虚拟机可能已经退出了
- Java自带的多线程框架,比如ExecutorService,会将守护线程转换为用户线程,所以如果要使用后台线程就不能用Java的线程池。
转载:https://blog.csdn.net/lc1010078424/article/details/79613348
Daemon Thread实际应用
web服务器中的Servlet
web服务器中的Servlet,容器启动时后台初始化一个服务线程,即调度线程,负责处理http请求;
每个请求过来调度线程从线程池中取出一个工作者线程来处理该请求,从而实现并发控制的目的。
Java的Timer
import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
public class TimerTaskRun {
public static void main(String[] args) {
System.out.println("系统当前时间:"+new Date());
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.SECOND, 10);
Date date = calendar.getTime();
MyTask task = new MyTask();
Timer timer = new Timer();
timer.schedule(task, date);
}
}
以上写法任务虽然运行完了,但进程还未销毁,为什么会出现这种情况呢?
可以看一下Timer的源码
public Timer() {
this("Timer-" + serialNumber());
}
public Timer(String name) {
thread.setName(name);
thread.start();
}
可以看出每创建一个Timer就是启动一个新的线程,那么启动的线程不是守护线程,所以一直运行。那我们该如何将 新创建的的Timer 改成守护线程呢?更改如上的代码:
public class TimerTaskRun {
public static void main(String[] args) {
System.out.println("系统当前时间:"+new Date());
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.SECOND, 10);
Date date = calendar.getTime();
MyTask task = new MyTask();
//设置Timer为true
Timer timer = new Timer(true);
timer.schedule(task, date);
}
}
如果只想执行一次等待执行完之后调用timer.cancel()。