Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程)
用户线程即运行在前台的线程,而守护线程是运行在后台的线程。 守护线程作用是为其他前台线程的运行提供便利服务,而且仅在普通、非守护线程仍然运行时才需要,比如垃圾回收线程就是一个守护线程。当VM检测仅剩一个守护线程,而用户线程都已经退出运行时,VM就会退出,因为没有如果没有了被守护这,也就没有继续运行程序的必要了。如果有非守护线程仍然存活,VM就不会退出。
守护线程并非只有虚拟机内部提供,用户在编写程序时也可以自己设置守护线程。用户可以用Thread的setDaemon(true)方法设置当前线程为守护线程。
虽然守护线程可能非常有用,但必须小心确保其他所有非守护线程消亡时,不会由于它的终止而产生任何危害。因为你不可能知道在所有的用户线程退出运行前,守护线程是否已经完成了预期的服务任务。一旦所有的用户线程退出了,虚拟机也就退出运行了。 因此,不要在守护线程中执行业务逻辑操作(比如对数据的读写等)。、
另外有几点需要注意:
Timer代码示例:
package day003; import java.util.Date; import java.util.TimerTask; /** * * 项目名称:JavaThread * 类名称:MyTask * 类描述: * 创建人:liuc * 创建时间:2018年3月19日 下午3:05:28 * 修改人:liuc * 修改时间:2018年3月19日 下午3:05:28 * 修改备注: * @version * */ public class MyTask extends TimerTask{ /** * (non-Javadoc) * @see java.util.TimerTask#run() */ public void run() { System.out.println("任务执行了,时间为:"+new Date()); } } ----------------------------------------------------------------------------------- package day003; import java.util.Calendar; import java.util.Date; import java.util.Timer; /** * * 项目名称:JavaThread * 类名称:TimerTaskRun * 类描述: * 创建人:liuc * 创建时间:2018年3月19日 下午3:08:01 * 修改人:liuc * 修改时间:2018年3月19日 下午3:08:01 * 修改备注: * @version * */ 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); } }
运行结果:
系统当前时间:Mon Mar 19 15:11:47 CST 2018
任务执行了,时间为:Mon Mar 19 15:11:57 CST 2018
任务虽然运行完了,但进程还未销毁,呈红色状态,为什么会出现这种情况呢?
可以看一下Timer的源码
/** * Creates a new timer. The associated thread does <i>not</i> * {@linkplain Thread#setDaemon run as a daemon}. */ public Timer() { this("Timer-" + serialNumber()); } /** * Creates a new timer whose associated thread has the specified name. * The associated thread does <i>not</i> * {@linkplain Thread#setDaemon run as a daemon}. * * @param name the name of the associated thread * @throws NullPointerException if {@code name} is null * @since 1.5 */ public Timer(String name) { thread.setName(name); thread.start(); }
可以看出每创建一个Timer就是启动一个新的线程,那么启动的线程不是守护线程,所以一直运行。那我们该如何将 新创建的的Timer改成守护线程呢?更改如上的代码:
package day003; import java.util.Calendar; import java.util.Date; import java.util.Timer; /** * * 项目名称:JavaThread * 类名称:TimerTaskRun * 类描述: * 创建人:liuc * 创建时间:2018年3月19日 下午3:08:01 * 修改人:liuc * 修改时间:2018年3月19日 下午3:08:01 * 修改备注: * @version * */ 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(true); timer.schedule(task, date); } }
运行结果如下:
系统当前时间:Mon Mar 19 15:21:42 CST 2018
守护线程中产生的线程也是守护线程
如下示例:
package day003; /** * * 项目名称:JavaThread * 类名称:Daemon * 类描述: * 创建人:liuc * 创建时间:2018年3月19日 下午3:30:53 * 修改人:liuc * 修改时间:2018年3月19日 下午3:30:53 * 修改备注: * @version * */ public class Daemon implements Runnable { private Thread[] t = new Thread[10]; /** * (non-Javadoc) * @see java.lang.Runnable#run() */ 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(); } } } ----------------------------------------------------------------------------------- package day003; /** * * 项目名称:JavaThread * 类名称:DaemonSpawn * 类描述: * 创建人:liuc * 创建时间:2018年3月19日 下午3:32:06 * 修改人:liuc * 修改时间:2018年3月19日 下午3:32:06 * 修改备注: * @version * */ public class DaemonSpawn implements Runnable { /** * (non-Javadoc) * @see java.lang.Runnable#run() */ public void run() { while (true) { Thread.yield(); } } } ----------------------------------------------------------------------------------- package day003; import java.util.concurrent.TimeUnit; /** * * 项目名称:JavaThread * 类名称:DaemonRun * 类描述: * 创建人:liuc * 创建时间:2018年3月19日 下午3:36:34 * 修改人:liuc * 修改时间:2018年3月19日 下午3:36:34 * 修改备注: * @version * */ public class DaemonRun { 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.
如果将mian函数中的TimeUnit.SECONDS.sleep(1);注释掉,看一下TimeUnit.SECONDS.sleep()的源码:
/** * Performs a {@link Thread#sleep(long, int) Thread.sleep} using * this time unit. * This is a convenience method that converts time arguments into the * form required by the {@code Thread.sleep} method. * * @param timeout the minimum time to sleep. If less than * or equal to zero, do not sleep at all. * @throws InterruptedException if interrupted while sleeping */ 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.
以上结果也说明了如果用户线程全部退出了,只剩下守护线程存在了,虚拟机也就退出了。
典型的守护线程是(GC)垃圾回收线程。
package day003; /** * * 项目名称:JavaThread * 类名称:MyThread * 类描述: * 创建人:liuc * 创建时间:2018年3月19日 下午3:50:12 * 修改人:liuc * 修改时间:2018年3月19日 下午3:50:12 * 修改备注: * @version * */ public class MyThread extends Thread{ private int i = 0; /** * (non-Javadoc) * @see java.lang.Thread#run() */ public void run() { super.run(); try { while (true) { i++; System.out.println("i="+i); Thread.sleep(1000); } } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) throws InterruptedException { MyThread daemonThread = new MyThread(); daemonThread.setDaemon(true); daemonThread.start(); Thread.sleep(5000); System.out.println("当main线程执行完毕,守护线程也停止了。"); } }
运行结果:
i=1
i=2
i=3
i=4
i=5
当main线程执行完毕,守护线程也停止了。
除 JVM 内部的守护线程外,用户可以通过以下方法设置守护线程:
public final void setDaemon(boolean on)
可以通过以下方法查询线程是否为守护线程:
public final boolean isDaemon()
关于守护线程的几个要点:
1、setDaemon 方法必须在 thread.start() 之前设置,否则会抛出 java.lang.IllegalThreadStateException 异常,不能将正在运行的常规线程设置为守护线程
package day003; /** * * 项目名称:JavaThread * 类名称:TestDaemon * 类描述: * 创建人:liuc * 创建时间:2018年3月19日 下午4:01:32 * 修改人:liuc * 修改时间:2018年3月19日 下午4:01:32 * 修改备注: * @version * */ public class TestDaemon { public static void main(String[] args) { Thread thread = new Thread(); thread.start(); thread.setDaemon(true); } }
运行结果:
Exception in thread "main" java.lang.IllegalThreadStateException
at java.lang.Thread.setDaemon(Thread.java:1359)
at day003.TestDaemon.main(TestDaemon.java:32)
2、不是所有的应用都可以分配给 Daemon 线程来进行服务,比如读写操作或者计算逻辑,因为在 Daemon 线程还没来的及进行操作时虚拟机可能已经退出了
package day003; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; /** * * 项目名称:JavaThread * 类名称:TestDaemon2 * 类描述: * 创建人:liuc * 创建时间:2018年3月19日 下午4:03:22 * 修改人:liuc * 修改时间:2018年3月19日 下午4:03:22 * 修改备注: * @version * */ public class TestDaemon2 extends Thread{ /** * (non-Javadoc) * @see java.lang.Thread#run() */ public void run() { super.run(); FileOutputStream outputStream = null; try { Thread.sleep(3000); File file = new File("daemon.txt"); outputStream = new FileOutputStream(file); outputStream.write("daemon".getBytes()); } catch (InterruptedException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (outputStream != null) { try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } public static void main(String[] args) { Thread thread = new TestDaemon2(); thread.setDaemon(true); thread.start(); }
以上代码中线程功能是向工程根目录的“daemon.txt”html" target="_blank">文件写入字符串“daemon”,实际运行结果发现并未成功写入,且未报任何错误,原因是写入文件的线程被设置为守护线程,该线程还在 sleep 过程中时所有用户线程就全部结束了,守护线程也会随着 JVM 一起退出。
如果将上面代码中的thread.setDaemon(true);注释掉,
public static void main(String[] args) { Thread thread = new TestDaemon2(); //thread.setDaemon(true); thread.start(); }
不将线程设置为守护线程可以在工程根目录的“daemon.txt”文件中看到字符串“daemon”
示例2:
package day003; /** * * 项目名称:JavaThread * 类名称:CustomThread * 类描述: * 创建人:liuc * 创建时间:2018年3月19日 下午4:16:42 * 修改人:liuc * 修改时间:2018年3月19日 下午4:16:42 * 修改备注: * @version * */ public class CustomThread extends Thread { /** * (non-Javadoc) * @see java.lang.Thread#run() */ public void run() { super.run(); for (int i = 0; i < 100; i++) { System.out.println("Daemon Thread : " + i); } } public static void main(String[] args) { Thread daemonThread = new CustomThread(); daemonThread.setDaemon(true); Thread userThread = new Thread(); daemonThread.start(); userThread.start(); } }
多次执行示例2代码,控制台要么不打印任何信息,要么打印一部分循环的输出信息就结束了,从运行结果可以看出,守护线程并未执行完成所有循环就结束了,因为用户线程在守护线程执行循环的过程中就已全部结束,守护线程也随着 JVM 一起结束。
以上所述是小编给大家介绍的java守护线程与非守护线程详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对小牛知识库网站的支持!
本文向大家介绍浅谈java的守护线程与非守护线程,包括了浅谈java的守护线程与非守护线程的使用技巧和注意事项,需要的朋友参考一下 最近重新研究Java基础知识,发现以前太多知识知识略略带过了,比较说Java的线程机制,在Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程) ,(PS:以前忽略了)。 估计学过Unix开发但是没有细致学习Java的同学们会
问题内容: 我正在做Java试卷,并且遇到了以下使我感到困惑的问题。 以下哪项是正确的?(选择所有适用项。) 答:当应用程序开始运行时,会有一个守护线程,其任务是执行main()。 B.当一个应用程序开始运行时,有一个非守护进程线程,其工作是执行main()。 C.由守护程序线程创建的线程最初也是守护程序线程。 D.由非守护程序线程创建的线程最初也是非守护程序线程。 关键答案是B,C,D,谁能告诉
本文向大家介绍详解Java线程-守护线程与用户线程,包括了详解Java线程-守护线程与用户线程的使用技巧和注意事项,需要的朋友参考一下 干java 开发这么多年, 之前一直没留意java 进程还区分守护进程和用户进程。守护进程这个概念最早还是在linux系统中接触的,直到近期使用java开发心跳检测功能时,使用Timer时才发现原来java也有守护线程的概念。 1. Java 线程 1.1 守护线
主要内容:1 什么是Java 守护线程,2 Java 守护线程的要点,3 Thread类的守护线程方法,3 Java 守护线程的例子1 什么是Java 守护线程 Java中的守护程序线程是一个服务提供者线程,向用户线程提供服务。它的寿命受到用户线程的控制,即当所有用户线程死亡时,JVM会自动终止该线程。 有许多自动运行的Java守护程序线程,例如gc,finalizer等。 您可以通过在cmd命令提示符下键入来查看所有详细信息。jconsole工具提供有关已加载类,内存使用情况,正在运行的线程等
本文向大家介绍python实现守护进程、守护线程、守护非守护并行,包括了python实现守护进程、守护线程、守护非守护并行的使用技巧和注意事项,需要的朋友参考一下 守护进程 1、守护子进程 主进程创建守护进程 其一:守护进程会在主进程代码执行结束后就终止 其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic processes are not allow
本文向大家介绍谈谈Java中的守护线程与普通线程,包括了谈谈Java中的守护线程与普通线程的使用技巧和注意事项,需要的朋友参考一下 守护线程与普通线程的唯一区别是:当JVM中所有的线程都是守护线程的时候,JVM就可以退出了;如果还有一个或以上的非守护线程则不会退出。(以上是针对正常退出,调用System.exit则必定会退出) 所以setDeamon(true)的唯一意义就是告诉JVM不需要等待它