学习笔记之线程、Thread类和线程终止相关整理(下)——线程异常&JVM停止

提到线程的中断在某些情况下会抛出InterruptedException异常,最终导致线程的终止。其实,线程也有可能由于其他异常原因造成终止,在某些情况下为了做一些妥善的处理,我们需要捕获这些异常情况。看下面代码,觉得会怎样?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class App {
     static class ExRun implements Runnable {
         public void run() {
             System.out.println( "throwing..." );
             throw new RuntimeException();
         }
     }
 
     public static void main(String[] args) {
         try {
             new Thread( new ExRun()).start();
         } catch (Exception e) {
             System.out.println( "runtime ex. catched!" );
         }
     }
}

会在控制台打出”runtime ex. catched!”?你确定么?不怕大家笑话,几年我第一眼看类似这段代码的时候,竟然也认为这很理所当然。其实,这也许不是年头的问题,是是否仔细思考和经验问题。只要仔细想一想,既然已经new了一个Thread并且让他start()去了,异常又不是在new的过程中或者start的过程中出来的,抛不抛异常关这个主线程什么事儿。

那如果一个被主线程创建的新线程除了什么“故障”,就让其悄悄地自生自灭么?有没有补救方案呢?有的。在JavaSE5之前,这个问题直接交给线程组(ThreadGroup)了事儿,但在这之后就有了java.lang.Thread$UncaughtExceptionHandler($是内部关系,后者是前者的内部类/接口)这么一个interface,里面只有一个方法:

void uncaughtException(Thread t, Throwable e);

这个方法就是负责处理异常的,Thread参数不说了,那这个Throwable又是啥,这个就是Exception的老爸了,不了解的异常类层次结构的,这儿您就可以查查去了。既然是Throwable,其实也就不只是Exception了,Error也会被处理到。

接着就简单说下怎么用,Thread类的两个方法:

  • public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh)
  • public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh)

第一个是实例方法,需要针对某个Thread对象使用,而第二个是静态方法,设置全局的默认处理方法,但这两个不冲突,在Thread中是两个独立的handler属性。但毕竟默认的和非默认的还不一样,有个顺序问题,这里又不得不再提到线程组(ThreadGroup)

在JavaSE5之前,没有UncaughtExceptionHandler这个东西,所以出错会直接交给Thread的ThreadGroup对象属性group处理,因为ThreadGroup有处理异常的方法,而UncaughtExceptionHandler出来后,ThreadGroup也实现了这个接口。按照文档注释说明:

  1. 优先检查线程的uncaughtExceptionHandler
  2. 否则交给group处理
  3. 最后才可能轮到defaultUncaughtExceptionHandler

而看源码中线程组的处理实际上是追溯一个parent(也是ThreadGroup类对象)链:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void uncaughtException(Thread t, Throwable e) {
     if (parent != null ) {
         parent.uncaughtException(t, e);
     } else {
         Thread.UncaughtExceptionHandler ueh =
             Thread.getDefaultUncaughtExceptionHandler();
         if (ueh != null ) {
             ueh.uncaughtException(t, e);
         } else if (!(e instanceof ThreadDeath)) {
             System.err.print( "Exception in thread \""
                              + t.getName() + "\" " );
             e.printStackTrace(System.err);
         }
     }
}

最后为空时,采用全局默认的的handler。

线程异常就说道这,已经说了不少了,下面简单说两句JVM的退出/停止。

通常我们在课本上都会知道java.lang.System类有个方法是exit(int),这个会让程序退出,而实际上这个方法实际上是java.lang.Runtime类exit(int)的封装(其实System还有像gc()等很多方法,实际执行者都是这个java.lang.Runtime类对象),意义是告诉当前运行环境关闭运行,也就通常被理解为JVM“关闭”。

说下Runtime类,这个类是个单例模式的类,即从头到尾程序只能获得同一个对象引用,即调用静态方法Runtime.getRuntime()所得。Runtime类负责和运行时环境相关的各种任务,有很多有趣的方法,比如很多应用(如Tomcat)中某些线程池中线程数目的确定和当前运行环境处理器个数有关,则可通过Runtime的availableProcessors()方法获得。这里重点说下exit()方法。

还有一个概念需要说下,就是ShutdownHook,这个听起来“高端洋气上档次”的东西实际上就是一个Thread,但它的特殊就在于是一个hook,在JVM关闭(exit)的时候才会执行到。就如线程和中断的关系一样,JVM和exit()也是一样,exit()的执行并不是虚拟机马上关掉,而是安排结束时所要执行任务,按文档说明分上下两个过程,前面的过程就是shutdown对应Thread执行的过程。要想让虚拟机优雅的结束,exit()安排执行shutdownHook清理环境是必要的,还说Tomcat吧,终止的时候也会执行注册过的hook。执行hook的时候,通常守护/非守护线程也都仍然可以执行,但对hookd的新增和移除则不可以了。

Runtime和shutdonwHook关联起来的方法,主要就是这俩了:

  • public void addShutdownHook(Thread hook)
  • public boolean removeShutdownHook(Thread hook)

具体用法不用多说估计看文档都能懂,需要注意的地方就是,想要remove必需在add的时候留有hook的Thread对象引用。

和Thread的stop()类似,Runtime也有halt()方法,但这是一个“暴力停机”的方法,不会考虑hook的执行,如果情况允许,exit()还是优雅的选择。

到此,对线程相关的基本介绍先告一段落,读者们觉得需要补充的地方,我真得是非常欢迎补充、拍砖和指正的,对你我的学习成长都有很大好处。:)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值