前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >守护线程和非守护线程_java守护线程的作用

守护线程和非守护线程_java守护线程的作用

作者头像
全栈程序员站长
发布2022-11-04 16:50:27
5480
发布2022-11-04 16:50:27
举报
文章被收录于专栏:全栈程序员必看

Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程)

用户线程即运行在前台的线程,而守护线程是运行在后台的线程。 守护线程作用是为其他前台线程的运行提供便利服务,而且仅在普通、非守护线程仍然运行时才需要,比如垃圾回收线程就是一个守护线程。当VM检测仅剩一个守护线程,而用户线程都已经退出运行时,VM就会退出,因为没有如果没有了被守护这,也就没有继续运行程序的必要了。如果有非守护线程仍然存活,VM就不会退出。

守护线程并非只有虚拟机内部提供,用户在编写程序时也可以自己设置守护线程。用户可以用Thread的setDaemon(true)方法设置当前线程为守护线程。

虽然守护线程可能非常有用,但必须小心确保其他所有非守护线程消亡时,不会由于它的终止而产生任何危害。因为你不可能知道在所有的用户线程退出运行前,守护线程是否已经完成了预期的服务任务。一旦所有的用户线程退出了,虚拟机也就退出运行了。 因此,不要在守护线程中执行业务逻辑操作(比如对数据的读写等)。、

另外有几点需要注意:

1、setDaemon(true)必须在调用线程的start()方法之前设置,否则会抛出IllegalThreadStateException异常。

2、在守护线程中产生的新线程也是守护线程。 3、 不要认为所有的应用都可以分配给守护线程来进行服务,比如读写操作或者计算逻辑。

Timer代码示例:

代码语言:javascript
复制
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);
	}
}

运行结果:

代码语言:javascript
复制
系统当前时间:Mon Mar 19 15:11:47 CST 2018
任务执行了,时间为:Mon Mar 19 15:11:57 CST 2018

任务虽然运行完了,但进程还未销毁,呈红色状态,为什么会出现这种情况呢?

这里写图片描述
这里写图片描述

可以看一下Timer的源码

代码语言:javascript
复制
   /**
     * 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改成守护线程呢?更改如上的代码:

代码语言:javascript
复制
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

这里写图片描述
这里写图片描述

守护线程中产生的线程也是守护线程 如下示例:

代码语言:javascript
复制
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);
	}
}

运行结果如图:

代码语言:javascript
复制
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()的源码:

代码语言:javascript
复制
/**
     * 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()的封装,提供了可读性更好的线程暂停操作 注释后代码运行如下:

代码语言:javascript
复制
d.isDaemon() = true.

以上结果也说明了如果用户线程全部退出了,只剩下守护线程存在了,虚拟机也就退出了。

典型的守护线程是(GC)垃圾回收线程。

代码语言:javascript
复制
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线程执行完毕,守护线程也停止了。");
	}
}

运行结果:

代码语言:javascript
复制
i=1
i=2
i=3
i=4
i=5
当main线程执行完毕,守护线程也停止了。

除 JVM 内部的守护线程外,用户可以通过以下方法设置守护线程:

代码语言:javascript
复制
public final void setDaemon(boolean on)

可以通过以下方法查询线程是否为守护线程:

代码语言:javascript
复制
public final boolean isDaemon()

关于守护线程的几个要点: 1、setDaemon 方法必须在 thread.start() 之前设置,否则会抛出 java.lang.IllegalThreadStateException 异常,不能将正在运行的常规线程设置为守护线程

代码语言:javascript
复制
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);
	}
}

运行结果:

代码语言:javascript
复制
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 线程还没来的及进行操作时虚拟机可能已经退出了

代码语言:javascript
复制
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);注释掉,

代码语言:javascript
复制
	public static void main(String[] args) {
	    Thread thread = new TestDaemon2();
	  //thread.setDaemon(true);
	    thread.start();
	}

不将线程设置为守护线程可以在工程根目录的“daemon.txt”文件中看到字符串“daemon”

这里写图片描述
这里写图片描述

示例2:

代码语言:javascript
复制
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 一起结束。

请关注我个人学习的公众号,公众号会不定期发布个人学习的内容记录!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/181681.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年10月15日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档