Java中有两类线程: User Thread(用户线程) Daemon Thread(守护线程)
守护线程也叫后台线程, 指的是程序在运行的时候后台提供一种通用服务的线程, 比如jvm中垃圾回收线程, 这种线程并不属于程序中不可或缺的部分. 因此当所有非守护线程结束时, 程序也就终止了, 同时会杀死进程中所有的守护线程. 反过来说, 只要有任何非守护线程在运行, 程序就不会终止.
守护线程和非守护线程的区别就是jvm的离开, 如果用户线程已经全部退出运行了, 只剩下守护线程存在了, 虚拟机也就退出了. 因为没有了被守护者, 守护线程也就没工作可做了, 也就没有继续运行程序的必要了.
将线程转换为守护线程可以通过调用Thread对象的setDaemon(true)方法来实现. 在使用守护线程时需要注意:
- thread.setDaemon(true)必须在thread.start()之前设置, 否则会抛出一个IllegalThreadStateException异常, 你不能把正在运行的常规线程设置为守护线程
- 在Daemon线程中产生的新线程也是Daemon的
- 守护线程应该永远不去访问固有资源, 如文件, 数据库, 因为它会在任何时候甚至在一个操作的中间发生中断.
- Timer代码:
import java.util.Date;
import java.util.TimerTask;
public class MyTask extends TimerTask
{
@Override
public void run() {
System.out.println("任务执行了,时间为:"+new Date());
}
主函数
import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
public class Test1 {
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);
}
}
运行结果:
当前时间为: Mon Jun 17 20:12:37 CST 2019
任务执行了, 时间为Mon Jun 17 20:12:47 CST 2019
任务虽然运行完了, 但进程还未销毁, 呈红色状态, 为什么会出现这种情况呢?
让我们看看Timer的源码
public Timer() {
this("Timer-" + serialNumber());
}
public Timer(String name) {
thread.setName(name);
thread.start();
}
可以看出每创建一个Timer就是启动一个新的线程, 那么启动的线程不是守护线程, 所以一直运行. 将新创建的Timer改成守护线程, 更改如上的代码
import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
public class Test1 {
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(true);
timer.schedule(task,date);
}
}
运行结果如下
当前时间为: Mon Jun 17 20:12:42 CST 2019
守护线程中产生的线程也就是守护线程
public class Daemon implements Runnable {
private Thread[] t = new Thread[10];
@Override
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();
}
}
}
类DaemonSpawn:
public class DaemonSpawn implements Runnable {
@Override
public void run() {
while (true) {
Thread.yield();
}
}
}
主函数:
import java.util.concurrent.TimeUnit;
public class Test1 {
public static void main(String[] args) throws InterruptedException {
Thread d = new Thread(new Daemon());
d.setDaemon(true); //必须在启动线程前调用
d.start();
System.out.println("d.isDaemon() = " + d.isDaemon() + ".");
TimeUnit.SECONDS.sleep(1);
}
}
运行结果:
d.isDaemon() = true.
DaemonSpawn 0 started.
DaemonSpawn 1 started.
DaemonSpawn 2 started.
DaemonSpawn 3 started.
DaemonSpawn 4 started.
DaemonSpawn 5 started.
DaemonSpawn 6 started.
DaemonSpawn 7 started.
DaemonSpawn 8 started.
DaemonSpawn 9 started.
t[0].isDaemon() = true.
t[1].isDaemon() = true.
t[2].isDaemon() = true.
t[3].isDaemon() = true.
t[4].isDaemon() = true.
t[5].isDaemon() = true.
t[6].isDaemon() = true.
t[7].isDaemon() = true.
t[8].isDaemon() = true.
t[9].isDaemon() = true.
Process finished with exit code 0
如果将main函数中的TimerUnit.SECONDS.sleep(1)注释掉, 看一下它的源码:
public void sleep(long timeout) throws InterruptedException {
if (timeout > 0) {
long ms = toMillis(timeout);
int ns = excessNanos(timeout, ms);
Thread.sleep(ms, ns);
}
}
其实就是对Thread.sleep()的封装, 提供了可读性更好的线程暂停操作, 注释后代码运行结果如下:
d.isDaemon() = true.
DaemonSpawn 0 started.
DaemonSpawn 1 started.
DaemonSpawn 2 started.
DaemonSpawn 3 started.
DaemonSpawn 4 started.
DaemonSpawn 5 started.
DaemonSpawn 6 started.
DaemonSpawn 7 started.
DaemonSpawn 8 started.
DaemonSpawn 9 started.
说明了如果用户线程全部退出了, 只剩下守护线程存在了, 虚拟机也就退出了