热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

Java守护线程实例详解_动力节点Java学院整理

在Java中有两类线程:UserThread(用户线程)、DaemonThread(守护线程)。下面通过本文给大家分享java守护线程实例详解,需要的朋友参考下吧

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

用个比较通俗的比如,任何一个守护线程都是整个JVM中所有非守护线程的保姆:

只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。

Daemon的作用是为其他线程的运行提供便利服务,守护线程最典型的应用就是 GC (垃圾回收器),它就是一个很称职的守护者。

User和Daemon两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:如果 User Thread已经全部退出运行了,只剩下Daemon Thread存在了,虚拟机也就退出了。 因为没有了被守护者,Daemon也就没有工作可做了,也就没有继续运行程序的必要了。

值得一提的是,守护线程并非只有虚拟机内部提供,用户在编写程序时也可以自己设置守护线程。下面的方法就是用来设置守护线程的。 

Thread daemOnTread= new Thread(); 
 // 设定 daemonThread 为 守护线程,default false(非守护线程) 
 daemonThread.setDaemon(true); 
 // 验证当前线程是否为守护线程,返回 true 则为守护线程 
 daemonThread.isDaemon(); 

这里有几点需要注意: 

(1) thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。

(2) 在Daemon线程中产生的新线程也是Daemon的。 

(3) 不要认为所有的应用都可以分配给Daemon来进行服务,比如读写操作或者计算逻辑。 

因为你不可能知道在所有的User完成之前,Daemon是否已经完成了预期的服务任务。一旦User退出了,可能大量数据还没有来得及读入或写出,计算任务也可能多次运行结果不一样。这对程序是毁灭性的。造成这个结果理由已经说过了:一旦所有User Thread离开了,虚拟机也就退出运行了。 

//完成文件输出的守护线程任务 
import java.io.*;  
class TestRunnable implements Runnable{  
 public void run(){  
    try{  
     Thread.sleep(1000);//守护线程阻塞1秒后运行  
     File f=new File("daemon.txt");  
     FileOutputStream os=new FileOutputStream(f,true);  
     os.write("daemon".getBytes());  
   }  
    catch(IOException e1){  
   e1.printStackTrace();  
    }  
    catch(InterruptedException e2){  
     e2.printStackTrace();  
   }  
 }  
}  
public class TestDemo2{  
 public static void main(String[] args) throws InterruptedException  
 {  
  Runnable tr=new TestRunnable();  
  Thread thread=new Thread(tr);  
    thread.setDaemon(true); //设置守护线程  
  thread.start(); //开始执行分进程  
 }  
}  
//运行结果:文件daemon.txt中没有"daemon"字符串。 

看到了吧,把输入输出逻辑包装进守护线程多么的可怕,字符串并没有写入指定文件。原因也很简单,直到主线程完成,守护线程仍处于1秒的阻塞状态。这个时候主线程很快就运行完了,虚拟机退出,Daemon停止服务,输出操作自然失败了。

public class Test { 
  public static void main(String args) { 
  Thread t1 = new MyCommon(); 
  Thread t2 = new Thread(new MyDaemon()); 
  t2.setDaemon(true); //设置为守护线程 
  t2.start(); 
  t1.start(); 
  } 
  } 
  class MyCommon extends Thread { 
  public void run() { 
  for (int i = 0; i <5; i++) { 
  System.out.println("线程1第" + i + "次执行!"); 
  try { 
  Thread.sleep(7); 
  } catch (InterruptedException e) { 
  e.printStackTrace(); 
  } 
  } 
  } 
  } 
class MyDaemon implements Runnable { 
  public void run() { 
  for (long i = 0; i <9999999L; i++) { 
  System.out.println("后台线程第" + i + "次执行!"); 
  try { 
  Thread.sleep(7); 
  } catch (InterruptedException e) { 
  e.printStackTrace(); 
  } 
  } 
  } 
  } 

后台线程第0次执行!
  线程1第0次执行! 
  线程1第1次执行! 
  后台线程第1次执行! 
  后台线程第2次执行! 
  线程1第2次执行! 
  线程1第3次执行! 
  后台线程第3次执行! 
  线程1第4次执行! 
  后台线程第4次执行! 
  后台线程第5次执行! 
  后台线程第6次执行! 
  后台线程第7次执行! 
  Process finished with exit code 0 

  从上面的执行结果可以看出: 

  前台线程是保证执行完毕的,后台线程还没有执行完毕就退出了。 

  实际上:JRE判断程序是否执行结束的标准是所有的前台执线程行完毕了,而不管后台线程的状态,因此,在使用后台线程时候一定要注意这个问题。 

补充说明:

定义:守护线程--也称“服务线程”,在没有用户线程可服务时会自动离开。

优先级:守护线程的优先级比较低,用于为系统中的其它对象和线程提供服务。

设置:通过setDaemon(true)来设置线程为“守护线程”;将一个用户线程设置为守护线程的方式是在 线程对象创建 之前 用线程对象的setDaemon方法。

example: 垃圾回收线程就是一个经典的守护线程,当我们的程序中不再有任何运行的Thread,程序就不会再产生垃圾,垃圾回收器也就无事可做,所以当垃圾回收线程是JVM上仅剩的线程时,垃圾回收线程会自动离开。它始终在低级别的状态中运行,用于实时监控和管理系统中的可回收资源。

生命周期:守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。也就是说守护线程不依赖于终端,但是依赖于系统,与系统“同生共死”。那Java的守护线程是什么样子的呢。当JVM中所有的线程都是守护线程的时候,JVM就可以退出了;如果还有一个
或以上的非守护线程则JVM不会退出。

实际应用例子:

在使用长连接的comet服务端推送技术中,消息推送线程设置为守护线程,服务于ChatServlet的servlet用户线程,在servlet的init启动消息线程,servlet一旦初始化后,一直存在服务器,servlet摧毁后,消息线程自动退出

容器收到一个Servlet请求,调度线程从线程池中选出一个工作者线程,将请求传递给该工作者线程,然后由该线程来执行Servlet的 service方法。当这个线程正在执行的时候,容器收到另外一个请求,调度线程同样从线程池中选出另一个工作者线程来服务新的请求,容器并不关心这个请求是否访问的是同一个Servlet.当容器同时收到对同一个Servlet的多个请求的时候,那么这个Servlet的service()方法将在多线程中并发执行。

Servlet容器默认采用单实例多线程的方式来处理请求,这样减少产生Servlet实例的开销,提升了对请求的响应时间,对于Tomcat可以在server.xml中通过元素设置线程池中线程的数目。
如图: 

为什么要用守护线程?

我们知道静态变量是ClassLoader级别的,如果Web应用程序停止,这些静态变量也会从JVM中清除。但是线程则是JVM级别的,如果你在Web 应用中启动一个线程,这个线程的生命周期并不会和Web应用程序保持同步。也就是说,即使你停止了Web应用,这个线程依旧是活跃的。正是因为这个很隐晦 的问题,所以很多有经验的开发者不太赞成在Web应用中私自启动线程。

如果我们手工使用JDK Timer(Quartz的Scheduler),在Web容器启动时启动Timer,当Web容器关闭时,除非你手工关闭这个Timer,否则Timer中的任务还会继续运行!

下面通过一个小例子来演示这个“诡异”的现象,我们通过ServletContextListener在Web容器启动时创建一个Timer并周期性地运行一个任务:

//代码清单StartCycleRunTask:容器监听器 
package com.bjpowernode.web; 
import java.util.Date; 
import java.util.Timer; 
import java.util.TimerTask; 
import javax.servlet.ServletContextEvent; 
import javax.servlet.ServletContextListener; 
public class StartCycleRunTask implements ServletContextListener ...{ 
 private Timer timer; 
 public void contextDestroyed(ServletContextEvent arg0) ...{ 
  // ②该方法在Web容器关闭时执行 
  System.out.println("Web应用程序启动关闭..."); 
 } 
 public void contextInitialized(ServletContextEvent arg0) ...{ 
   //②在Web容器启动时自动执行该方法 
  System.out.println("Web应用程序启动..."); 
  timer = new Timer();//②-1:创建一个Timer,Timer内部自动创建一个背景线程 
  TimerTask task = new SimpleTimerTask(); 
  timer.schedule(task, 1000L, 5000L); //②-2:注册一个5秒钟运行一次的任务 
 } 
} 
class SimpleTimerTask extends TimerTask ...{//③任务 
 private int count; 
 public void run() ...{ 
  System.out.println((++count)+"execute task..."+(new Date())); 
 } 
} 

在web.xml中声明这个Web容器监听器:

<&#63;xml version="1.0" encoding="UTF-8"&#63;>
 
com.bjpowernode.web.StartCycleRunTask 
 
 

在Tomcat中部署这个Web应用并启动后,你将看到任务每隔5秒钟执行一次。 

运行一段时间后,登录Tomcat管理后台,将对应的Web应用(chapter13)关闭。 

转到Tomcat控制台,你将看到虽然Web应用已经关闭,但Timer任务还在我行我素地执行如故——舞台已经拆除,戏子继续表演: 

我们可以通过改变清单StartCycleRunTask的代码,在contextDestroyed(ServletContextEvent arg0)中添加timer.cancel()代码,在Web容器关闭后手工停止Timer来结束任务。

Spring为JDK Timer和Quartz Scheduler所提供的TimerFactoryBean和SchedulerFactoryBean能够和Spring容器的生命周期关联,在 Spring容器启动时启动调度器,而在Spring容器关闭时,停止调度器。所以在Spring中通过这两个FactoryBean配置调度器,再从 Spring IoC中获取调度器引用进行任务调度将不会出现这种Web容器关闭而任务依然运行的问题。而如果你在程序中直接使用Timer或Scheduler,如不 进行额外的处理,将会出现这一问题。 


推荐阅读
  • SpringMVC工作流程概述
    SpringMVC工作流程概述 ... [详细]
  • Servlet多用户登录时HttpSession会话信息覆盖问题的解决方案
    本文讨论了在Servlet多用户登录时可能出现的HttpSession会话信息覆盖问题,并提供了解决方案。通过分析JSESSIONID的作用机制和编码方式,我们可以得出每个HttpSession对象都是通过客户端发送的唯一JSESSIONID来识别的,因此无需担心会话信息被覆盖的问题。需要注意的是,本文讨论的是多个客户端级别上的多用户登录,而非同一个浏览器级别上的多用户登录。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • Oracle优化新常态的五大禁止及其性能隐患
    本文介绍了Oracle优化新常态中的五大禁止措施,包括禁止外键、禁止视图、禁止触发器、禁止存储过程和禁止JOB,并分析了这些禁止措施可能带来的性能隐患。文章还讨论了这些禁止措施在C/S架构和B/S架构中的不同应用情况,并提出了解决方案。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • Tomcat/Jetty为何选择扩展线程池而不是使用JDK原生线程池?
    本文探讨了Tomcat和Jetty选择扩展线程池而不是使用JDK原生线程池的原因。通过比较IO密集型任务和CPU密集型任务的特点,解释了为何Tomcat和Jetty需要扩展线程池来提高并发度和任务处理速度。同时,介绍了JDK原生线程池的工作流程。 ... [详细]
  • 标题: ... [详细]
  • 本文总结了淘淘商城项目的功能和架构,并介绍了传统架构中遇到的session共享问题及解决方法。淘淘商城是一个综合性的B2C平台,类似京东商城、天猫商城,会员可以在商城浏览商品、下订单,管理员、运营可以在平台后台管理系统中管理商品、订单、会员等。商城的架构包括后台管理系统、前台系统、会员系统、订单系统、搜索系统和单点登录系统。在传统架构中,可以采用tomcat集群解决并发量高的问题,但由于session共享的限制,集群数量有限。本文探讨了如何解决session共享的问题。 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • iOS超签签名服务器搭建及其优劣势
    本文介绍了搭建iOS超签签名服务器的原因和优势,包括不掉签、用户可以直接安装不需要信任、体验好等。同时也提到了超签的劣势,即一个证书只能安装100个,成本较高。文章还详细介绍了超签的实现原理,包括用户请求服务器安装mobileconfig文件、服务器调用苹果接口添加udid等步骤。最后,还提到了生成mobileconfig文件和导出AppleWorldwideDeveloperRelationsCertificationAuthority证书的方法。 ... [详细]
  • MySQL语句大全:创建、授权、查询、修改等【MySQL】的使用方法详解
    本文详细介绍了MySQL语句的使用方法,包括创建用户、授权、查询、修改等操作。通过连接MySQL数据库,可以使用命令创建用户,并指定该用户在哪个主机上可以登录。同时,还可以设置用户的登录密码。通过本文,您可以全面了解MySQL语句的使用方法。 ... [详细]
  • 分享css中提升优先级属性!important的用法总结
    web前端|css教程css!importantweb前端-css教程本文分享css中提升优先级属性!important的用法总结微信门店展示源码,vscode如何管理站点,ubu ... [详细]
  • 开发笔记:spring boot项目打成war包部署到服务器的步骤与注意事项
    本文介绍了将spring boot项目打成war包并部署到服务器的步骤与注意事项。通过本文的学习,读者可以了解到如何将spring boot项目打包成war包,并成功地部署到服务器上。 ... [详细]
  • 在IDEA中运行CAS服务器的配置方法
    本文介绍了在IDEA中运行CAS服务器的配置方法,包括下载CAS模板Overlay Template、解压并添加项目、配置tomcat、运行CAS服务器等步骤。通过本文的指导,读者可以轻松在IDEA中进行CAS服务器的运行和配置。 ... [详细]
  • 项目运行环境配置及可行性分析
    本文介绍了项目运行环境配置的要求,包括Jdk1.8、Tomcat7.0、Mysql、HBuilderX等工具的使用。同时对项目的技术可行性、操作可行性、经济可行性、时间可行性和法律可行性进行了分析。通过对数据库的设计和功能模块的设计,确保系统的完整性和安全性。在系统登录、系统功能模块、管理员功能模块等方面进行了详细的介绍和展示。最后提供了JAVA毕设帮助、指导、源码分享和调试部署的服务。 ... [详细]
author-avatar
gpik40799
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有