【java 多线程】守护线程与非守护线程的详解

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

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

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

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

另外有几点需要注意:

  1. 1、setDaemon(true)必须在调用线程的start()方法之前设置,否则会抛出IllegalThreadStateException异常。
  2. 2、在守护线程中产生的新线程也是守护线程。
  3. 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守护线程与非守护线程详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • Java中对AtomicInteger和int值在多线程下递增操作的测试

    Java针对多线程下的数值安全计数器设计了一些类,这些类叫做原子类,其中一部分如下: java.util.concurrent.atomic.AtomicBoolean; java.util.concurrent.atomic.AtomicInteger; java.util.concurrent.atomic.AtomicLong; java.util.concurrent.atomic.AtomicReference; 下面是一个对比  AtomicInteger 与 普通 int 值在多线

  • java多线程处理执行solr创建索引示例

    复制代码 代码如下: public class SolrIndexer implements Indexer, Searcher, DisposableBean { //~ Static fields/initializers ============================================= static final Logger logger = LoggerFactory.getLogger(SolrIndexer.class); private static fi

  • 浅谈java的守护线程与非守护线程

    最近重新研究Java基础知识,发现以前太多知识知识略略带过了,比较说Java的线程机制,在Java中有两类线程:User Thread(用户线程).Daemon Thread(守护线程) ,(PS:以前忽略了). 估计学过Unix开发但是没有细致学习Java的同学们会疑惑了,操作系统里面是没有所谓的守护线程的概念,只有守护进程一说,但是Java语言机制是构建在JVM的基础之上的,意思是Java平台把操作系统的底层给屏蔽起来,所以它可以在它自己的虚拟的平台里面构造出对自己有利的机制,而语言或者说平

  • 详解三种java实现多线程的方式

    java中实现多线程的方法有两种:继承Thread类和实现runnable接口. 1.继承Thread类,重写父类run()方法 public class thread1 extends Thread { public void run() { for (int i = 0; i < 10000; i++) { System.out.println("我是线程"+this.getId()); } } public static void main(String[] args) {

  • Java多线程实现异步调用的方法

    在JAVA平台,实现异步调用的角色有如下三个角色:调用者 提货单   真实数据 一个调用者在调用耗时操作,不能立即返回数据时,先返回一个提货单.然后在过一断时间后凭提货单来获取真正的数据. 去蛋糕店买蛋糕,不需要等蛋糕做出来(假设现做要很长时间),只需要领个提货单就可以了(去干别的事情),等到蛋糕做好了,再拿提货单取蛋糕就可以了. public class Main { public static void main(String[] args) { System.out.println("ma

  • java中thread线程start和run的区别

    最近看到一个题目,代码如下: 复制代码 代码如下: public static void main(String args[]) {Thread t = new Thread() {public void run() {pong();}}; t.run();System.out.println("ping");}static void pong() {System.out.println("pong");} 问,结果会输出什么? 我运行了很多次,结果都是pong p

  • 深入理解JAVA多线程之线程间的通信方式

    一,介绍 本总结我对于JAVA多线程中线程之间的通信方式的理解,主要以代码结合文字的方式来讨论线程间的通信,故摘抄了书中的一些示例代码. 二,线程间的通信方式 ①同步 这里讲的同步是指多个线程通过synchronized关键字这种方式来实现线程间的通信. 参考示例: public class MyObject { synchronized public void methodA() { //do something.... } synchronized public void methodB()

  • java多线程返回值使用示例(callable与futuretask)

    Callable接口类似于Runnable,从名字就可以看出来了,但是Runnable不会返回结果,并且无法抛出返回结果的异常,而Callable功能更强大一些,被线程执行后,可以返回值,这个返回值可以被Future拿到,也就是说,Future可以拿到异步执行任务的返回值,下面来看一个简单的例子 复制代码 代码如下: package com.future.test; import java.io.FileNotFoundException;import java.io.IOException;i

  • 详解Java编程中线程的挂起、恢复和终止的方法

    有时,线程的挂起是很有用的.例如,一个独立的线程可以用来显示当日的时间.如果用户不希望用时钟,线程被挂起.在任何情形下,挂起线程是很简单的,一旦挂起,重新启动线程也是一件简单的事. 挂起,终止和恢复线程机制在Java 2和早期版本中有所不同.尽管你运用Java 2的途径编写代码,你仍需了解这些操作在早期Java环境下是如何完成的.例如,你也许需要更新或维护老的代码.你也需要了解为什么Java 2会有这样的变化.因为这些原因,下面内容描述了执行线程控制的原始方法,接着是Java 2的方法. Jav

  • java多线程解决生产者消费者问题

    本文实例讲述了java多线程解决生产者消费者问题的方法.分享给大家供大家参考.具体分析如下: 题目是这样的: 采用Java 多线程技术,设计实现一个符合生产者和消费者问题的程序.对一个对象(枪膛)进行操作,其最大容量是12颗子弹.生产者线程是一个压入线程,它不断向枪膛中压入子弹:消费者线程是一个射出线程,它不断从枪膛中射出子弹. 要求: (1)给出分析过程说明. (2)程序输出,要模拟体现对枪膛的压入和射出操作: (2)设计程序时应考虑到两个线程的同步问题. 这个和著名的生产者消费者问题几乎是一

随机推荐