1,首先什么是守护线程,什么是非守护线程呢

下面是网上资料总结如下:

Java有两种Thread:“守护线程Daemon”(守护线程)与“用户线程User”(非守护线程)。

从字面上我们很容易将守护线程理解成是由虚拟机(virtual machine)在内部创建的,而用户线程则是自己所创建的。事实并不是这样,任何线程都可以是“守护线程Daemon”或“用户线程User”。他们在几乎每个方面都是相同的,唯一的区别是判断虚拟机何时离开:

用户线程:Java虚拟机在它所有非守护线程已经离开后自动离开。

守护线程:守护线程则是用来服务用户线程的,如果没有其他用户线程在运行,那么就没有可服务对象,也就没有理由继续下去。

 

守护线程与普通线程的唯一区别是:当JVM中所有的线程都是守护线程的时候,JVM就可以退出了;如果还有一个或以上的非守护线程则不会退出。(以上是针对正常退出,调用System.exit则必定会退出)  

当然我们也可以把一个线程设置成一个守护线程 方法就是 对象线程.setDaemon(true);

 

举个例子:就像   天上人间的保安 (守护线程),里面有牌位姑娘(非守护线程),他们是可以同时干着各自的活儿,但是 姑娘们要是都被JC带走了,那么门口的保安也就没有存在的意义了。

 

 2,下面介绍addShutdownHook方法注册新的虚拟机来关闭钩子

 

public void addShutdownHook(Thread hook)

Java 虚拟机会为了响应以下两类事件而关闭:

 

1程序正常退出,这发生在最后的非守护线程退出时,或者在调用 exit(等同于 System.exit)方法时。

2为响应用户中断而终止 虚拟机,如键入 ^C;或发生系统事件,比如用户注销或系统关闭。

 

关闭钩子 只是一个已初始化但尚未启动的线程。虚拟机开始启用其关闭序列时,它会以某种未指定的顺序启动所有已注册的关闭钩子,并让它们同时运行。运行完所有的钩子后,如果已启用退出终结,那么虚拟机接着会运行所有未调用的终结方法。最后,虚拟机会暂停。注意,关闭序列期间会继续运行守护线程,如果通过调用 exit 方法来发起关闭序列,那么也会继续运行非守护线程。

 

一旦开始了关闭序列,则只能通过调用 halt 方法来停止这个序列,此方法可强行终止虚拟机。

 

一旦开始了关闭序列,则不可能注册新的关闭钩子或取消注册先前已注册的钩子。尝试执行这些操作会导致抛出 IllegalStateException。

 

关闭钩子可在虚拟机生命周期中的特定时间运行,因此应保护性地对其进行编码。特别是应将关闭钩子编写为线程安全的,并尽可能地避免死锁。关闭钩子还应该不盲目地依靠某些服务,这些服务可能已注册了自己的关闭钩子,所以其本身可能正处于关闭进程中。例如,试图使用其他基于线程的服务(如 AWT 事件指派线程)可能导致死锁。

 

关闭钩子应该快速地完成其工作。当程序调用 exit 时,虚拟机应该迅速地关闭并退出。由于用户注销或系统关闭而终止虚拟机时,底层的操作系统可能只允许在固定的时间内关闭并退出。因此在关闭钩子中尝试进行任何用户交互或执行长时间的计算都是不明智的 

 

 3,为您的Java应用程序添加退出事件处理addShutdownHook

 

一个完整的Java应用程序,通常至少要有一个应用程序的结束点。对于一般程序来说,系统开发者根据需要和个人的偏好,会在程序结束位置,通过添加System.exit(0),或System.out(-1),来结束程序,或不加这些指令,让程序自然运行到结束。

对于简单的应用系统,我们直接可以在System.exit(0)代码执行前,添加需要在应用程序退出前需要完成的工作,如:关闭网络连接,关闭数据库连接等。

然而,对于比较复杂的多线程应用,线程运行的状态较复杂,我们就很难预料程序何时结束,如何能在应用程序结束事件到来时,处理我们要做的工作呢?这就用到了Java对应用程序的退出的事件出处理机制。

对当前应用程序对象的获得,Java通过Runtime静态方法:Runtime.getRuntime()通过Runtime的 void addShutdownHook(Thread hook) 法向Java虚拟机注册一个shutdown钩子事件,这样一旦程序结束事件到来时,就运行线程hook,我们在实际应用时候,只要将程序需要完成之前做的一些工作直接通过线程hook来完成。具体演示代码如下:

本程序仅演示,如何在Java应用程序中添加系统退出事件处理机制

import java.util.*;
import java.io.*;

public class Untitled1 {
    public Untitled1() {
        doShutDownWork();
    }
   
    private void doShutDownWork() {
        Runtime.getRuntime().addShutdownHook(new Thread() { 
             public void run() {
                   try {
                        FileWriter fw = new FileWriter("d://t.log");
                        System.out.println("I'm going to end");
                        fw.write("the application ended! " + (new Date()).toString());

                        fw.close();
                  } catch (IOException ex) { }
            }
       });
    }
   
    public static void main(String[] args) {
         Untitled1 untitled11 = new Untitled1();
         long s = System.currentTimeMillis();
         for (int i = 0; i < 1000000000; i++) { //在这里增添您需要处理代码 }
                long se = System.currentTimeMillis();
                System.out.println(se - s);
          }
     }
}


在上述程序中,我们可以看到通过在程序中增加Runtime.getRuntime().addShutdownHook(new Thread()) 事件监听,捕获系统退出消息到来,然后,执行我们所需要完成工作,从而使我们的程序更健壮!Java addShutdownHook ---程序出错退出终极处理办法   

在<Java多线程设计模式>里提到有关终止处理的方法addShutdownHook().这个方法会在Java执行环境全部结束时 (调用System.exit方法时,或者所有非守护线程都结束时),调用指定线程的start方法(这时候该线程称为shutdown hook),使用该方法可以编写整个程序的终止处理.
    简单示例:
Java代码
public class Main2 {   
 
    public static void main(String[] args) {   
        System.out.println("main: BEGIN");   
           
        // set shutdown hook   
        Runtime.getRuntime().addShutdownHook(   
            new Thread() {   
                public void run() {   
                    System.out.println(Thread.currentThread().getName() + ": Shutdown hook.");   
                }   
        });   
           
        System.out.println("main: SLEEP..");   
           
        // force quit after 3 sec.   
        try {   
            Thread.sleep(3000);   
        } catch (InterruptedException e) {   
            // TODO: handle exception   
        }   
           
        System.out.println("main: EXIT");   
        // force quit   
//      System.exit(0);   
        Runtime.getRuntime().exit(0);   
 
        // unreachable code   
        System.out.println("main: END");   
    }   
}  

    关于守护线程:
    Java有两种Thread:“守护线程Daemon”与“用户线程User”。
    我们之前看到的例子都是用户,守护线程是一种“在后台提供通用性支持”的线程,它并不属于程序本体。
    从字面上我们很容易将守护线程理解成是由虚拟机(virtual machine)在内部创建的,而用户线程则是自己所创建的。事实并不是这样,任何线程都可以是“守护线程Daemon”或“用户线程User”。他们在几乎每个方面都是相同的,唯一的区别是判断虚拟机何时离开:
    用户线程:Java虚拟机在它所有非守护线程已经离开后自动离开。
    守护线程:守护线程则是用来服务用户线程的,如果没有其他用户线程在运行,那么就没有可服务对象,也就没有理由继续下去。
    setDaemon(boolean on)方法可以方便的设置线程的Daemon模式,true为Daemon模式,false为User模式。setDaemon(boolean on)方法必须在线程启动之前调用,当线程正在运行时调用会产生异常。isDaemon方法将测试该线程是否为守护线程。值得一提的是,当你在一个守护线程中产生了其他线程,那么这些新产生的线程不用设置Daemon属性,都将是守护线程,用户线程同样。
    例:我们所熟悉的Java垃圾回收线程就是一个典型的守护线程,当我们的程序中不再有任何运行中的Thread,程序就不会再产生垃圾,垃圾回收器也就无事可做,所以当垃圾回收线程是Java虚拟机上仅剩的线程时,Java虚拟机会自动离开。
    守护线程是为其他线程的运行提供便利的线程。守护线程不会阻止程序的终止。
    非守护线程包括常规的用户线程或诸如用于处理GUI事件的事件调度线程。
    程序可以包含守护线程和非守护线程。
    当程序只有守护线程时,该程序便可以结束运行。
    如果要使一个线程成为守护线程,则必须在调用它的start方法之前进行设置(通过以true作为参数调用线程的setDaemon方法,可以将该线程定义为一个守护线程),否则会抛出IllegalThreadStateException异常。如果线程是守护线程,则isDaemon方法返回真。
    注:1、如果在线程已经启动后,再试图使该线程成为守护线程,则会导致IllegalThreadStateException异常。
     2、事件调度线程是一个无穷循环的线程,而不是守护线程。因而,在基于窗口的应用程序调用System类的exit方法之前,事件调度线程不会终止。
     3、不能将关键任务分配给守护线程。这些任务将会在事先没有警告的情况下终止,这可能导致不能正确地完成它们

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐