【java 多线程】守护线程与非守护线程的详解
作者:紫仙空灵 发布时间:2023-11-29 01:35:29
Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程)
用户线程即运行在前台的线程,而守护线程是运行在后台的线程。 守护线程作用是为其他前台线程的运行提供便利服务,而且仅在普通、非守护线程仍然运行时才需要,比如垃圾回收线程就是一个守护线程。当VM检测仅剩一个守护线程,而用户线程都已经退出运行时,VM就会退出,因为没有如果没有了被守护这,也就没有继续运行程序的必要了。如果有非守护线程仍然存活,VM就不会退出。
守护线程并非只有虚拟机内部提供,用户在编写程序时也可以自己设置守护线程。用户可以用Thread的setDaemon(true)方法设置当前线程为守护线程。
虽然守护线程可能非常有用,但必须小心确保其他所有非守护线程消亡时,不会由于它的终止而产生任何危害。因为你不可能知道在所有的用户线程退出运行前,守护线程是否已经完成了预期的服务任务。一旦所有的用户线程退出了,虚拟机也就退出运行了。 因此,不要在守护线程中执行业务逻辑操作(比如对数据的读写等)。、
另外有几点需要注意:
1、setDaemon(true)必须在调用线程的start()方法之前设置,否则会抛出IllegalThreadStateException异常。
2、在守护线程中产生的新线程也是守护线程。
3、 不要认为所有的应用都可以分配给守护线程来进行服务,比如读写操作或者计算逻辑。
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”文件写入字符串“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守护线程与非守护线程详解整合网站的支持!
来源:https://blog.csdn.net/lc1010078424/article/details/79613348
猜你喜欢
- 所谓回调,就是客户程序C调用服务程序S中的某个方法A,然后S又在某个时候反过来调用C中的某个方法B,对于C来说,这个B便叫做回调方法。下面看
- 一、简单介绍翻看Spring的源码时,发现@Bean注解的源码上标注了Since: 3.0,也就是说,@Bean注解是Spring从3.0版
- 本文实例讲述了C#创建临时文件的方法。分享给大家供大家参考。具体分析如下:C#可以通过Path.GetTempFileName获得一个临时文
- 1、前言当提及如何终止一个线程时,部分读者通常立马想到的方法肯定是stop(),但是stop()方法并不被推荐使用(很多规范中是禁止使用的)
- 1、找准入口,使用ClassPathXmlApplicationContext的构造方法加载配置文件,用于加载classPath下的配置文件
- 使用场景EntityListeners在jpa中使用,如果你是mybatis是不可以用的它的意义对实体属性变化的跟踪,它提供了保存前,保存后
- 一:需求当小数位很多的时候,小数位后面可能有一些多余的0并没有任何实际意义。所以在某些业务需求下可以去掉这些多余的0。例如:0.2000可以
- JavaWeb登录界面登录失败在同一页面进行提示方法使用JSP 通过提交表单方式 判断账号密码是否正确 不正确则调用req.setAttri
- 背景Swagger 可以提供 API 操作的测试文档,本文记录 Swagger 使用过程中遇到的两个小问题:全局响应结果进行包装后导致 Sw
- SpringBoot的持久化层可以是Spring内置的轻量级JdbcTemplate、也可以是Hibernate或Mybatis等等,只需要
- 什么是WebSocket?WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信—
- JPA Specification常用查询+排序1.第一步:继承父类public interface TblCarton2RCardLogR
- 注解从java5开始加入这一特性,发展到现在已然是遍地开花,在很多框架中得到了广泛的使用,用来简化程序中的配置。那充满争议的类型注解究竟是什
- 什么是JMMJMM全称Java Memory Model, 中文翻译Java内存模型,一种符合内存模型规范的,屏蔽了各种硬件和操作系统的访问
- 介绍Spring Cache是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能。Spring Cache提供了一
- 在spring 3.2 及以后版本中增加了对请求的异步处理,旨在提高请求的处理速度降低服务性能消耗。在我们的请求中做了耗时处理,当并发请求的
- 在该示例中,阐述了SpringMVC如何上传文件。1、上传页面upload.jsp<body> <form
- 逆时针画圆弧,原理:将360度分割成36份,分别标出每10度角度时的坐标点,然后将每个点连接起来。 #include <io
- 一、介绍在实际的软件项目开发过程中,我可以很负责任的跟大家说,如果你真的实际写代码的时间超过5年,你对增删改查这类简单的功能需求开发,可以说
- Java7引入了Fork Join的概念,来更好的支持并行运算。顾名思义,Fork Join类似与流程语言的分支,合并的概念。也就是说Jav