异常处理器详解 Java多线程异常处理机制 多线程中篇(四)

在Thread中有异常处理器相关的方法
image_5c5bc46a_7657
在ThreadGroup中也有相关的异常处理方法
image_5c5bc46a_717e

示例

未检查异常

image_5c5bc46a_25c1
对于未检查异常,将会直接宕掉,主线程则继续运行,程序会继续运行
在主线程中能不能捕获呢?
咱们简单粗暴一点,直接所有包到try catch中
image_5c5bc46a_1f01
你会发现,然而并无什么卵用,主线程中的try catch并不会获得什么信息,跟原来的结果仍是同样的,线程直接宕掉

已检查异常

image_5c5bc46a_1b9a
对于已检查的异常,run方法自己是不支持抛出的,上面代码中,想要throws,IDE提示异常,从run方法能够看得出来
run方法自己是不支持throws的(签名中没有throws)
image_5c5bc46a_2d74
因此怎么办?
既然是已检查异常,确定是要处理的,既然不能丢出去,就只有一个办法了,那就是本身捕获,放置在try catch中
image_5c5bc46a_5ee4

小结

在run方法中是不可以抛出异常的,若是是已检查的异常,那么必须进行try catch
对于未检查的异常,若是没有进行处理,一旦抛出线程将会宕掉,并且在主线程中并不能捕获到这个异常
难道对于未检查的异常也都是try catch吗?(固然,这是一种方式)
还有没有其余解决方案?

异常处理器

在Java线程的run方法中,对于未检查异常,借助于异常处理器进行处理的
字面意思,直接理解为处理异常的方法,那么如何配置这个处理异常的方法呢?如何设置,又是如何调用?
UncaughtExceptionHandler,是Thread的内部接口(1.8中已经设置为函数式接口)
image_5c5bc46a_5bcf
Thread内部有两个变量,用于记录异常处理器
image_5c5bc46a_1ec8
对于两个set方法,没有什么特别的,主要就是设置这两个内部变量
image_5c5bc46a_205e
对于getUncaughtExceptionHandler方法,若是当前非空,那么返回当前,不然,将返回当前线程组,很显然,ThreadGroup实现了Thread.UncaughtExceptionHandler
image_5c5bc46b_1509
对于getDefaultUncaughtExceptionHandler,这是简单的返回内部变量
image_5c5bc46b_7ff5
此时咱们大体了解到了这几个方法,内部有两个UncaughtExceptionHandler异常处理器,分别都有getter和setter方法
setter方法都是直接设置
getDefaultUncaughtExceptionHandler是直接获取
getUncaughtExceptionHandler若是非空那么直接获取,不然将会返回当前线程组,当前线程组也实现了Thread.UncaughtExceptionHandler,内部实现了方法public void uncaughtException(Thread t, Throwable e)
换句话说,线程组内部实现了一个线程处理器

两个处理器含义

咱们看到了表面的样子,可是这两个内部变量到底干吗的?
对于defaultUncaughtExceptionHandler,表示的是应用程序默认的,应用程序默认的,也就是整个程序使用的,能够看获得,对于他的getter和setter以及自身,都是static修饰的
对于uncaughtExceptionHandler,属于实例方法,也就是说每一个线程能够拥有一个
简言之:每一个线程均可以有一个uncaughtExceptionHandler,整个应用能够有一个defaultUncaughtExceptionHandler
全局和个体的关系,就如同咱们平时见到的其余概念同样,若是单独设置了,那么就使用本身的,若是没有设置就走全局的
既能够单独设置,又能够全局设置(没有设置的才会走全局),既能够保障灵活性,有可以对于那些没设置的提供统一配置,好比统一将异常信息写入文件等,也有诸多应用场景与好处

异常处理器处理逻辑

当异常发生时,JVM会调用异常分发处理器,也就是借助于getUncaughtExceptionHandler方法,获取异常处理器,而后执行他的uncaughtException方法
第一个参数就是当前线程this,第二个参数就是异常对象
看注释:JVM调用
image_5c5bc46b_4da
 
因此关键点在于getUncaughtExceptionHandler返回什么异常处理器,咱们再回过头来看下源代码
image_5c5bc46b_3e2
若是已经设置,那么将会直接返回;
若是没有设置,将会返回当前线程组(前面说了ThreadGroup实现了Thread.UncaughtExceptionHandler)
当调用ThreadGroup的uncaughtException方法时,如上图下半部分
若是他的父线程组重写了uncaughtException方法,那么将会调用他的父线程组的方法,若是父亲节点没有重写,爷爷节点重写了将会调用爷爷的,以此类推
可是若是全部的祖先线程组都没有重写呢?很显然,全部的方法代码都是上面这样子的(上图下半部分),将会递归到顶级线程组,而后不知足parent,而后走到else,这中间什么有意义的事情都没有作
在else中,会首先获取应用默认的异常处理器,若是仍旧是没有设置
很差意思,直接转到system.err了
image_5c5bc46b_6c61

代码示例

image_5c5bc46b_43cf
从上面的示例能够看得出来,尽管仍就出现了异常,咱们可以进行信息获取与感知,不会直接宕掉了
若是先start,而后在设置异常处理器会发生什么?
image_5c5bc46b_6b91
能够看获得,线程仍旧是直接宕掉,异常处理器无效,因此setUncaughtExceptionHandler方法必须在start方法前调用!

总结

在Thread中的run方法,不可以抛出异常,只能进行捕获
  • 对于已检查异常,必须捕获
  • 对于未检查异常,你也能够进行try catch,可是代码始终包裹在try中,真的好吗?
  • 还另外提供了异常处理器机制用于处理未检查异常
有两种异常处理器:
线程自身的处理器和全局的异常处理器
  1. 若是设置了异常处理器uncaughtExceptionHandler,那么将会使用这个
  2. 若是没设置,将会在祖先线程组中查找第一个重写了uncaughtException的线程组,而后调用他的uncaughtException方法
  3. 若是都没有重写,那么使用应用默认的全局异常处理器defaultUncaughtExceptionHandler
  4. 若是仍是没有设置,直接标准错误打印信息
若是想要设置本身的异常处理器,能够经过对应的setter方法进行设置,若是想要设置全局的能够调用静态方法进行设置
异常处理器Thread.UncaughtExceptionHandler是一个函数式接口,因此后续,你可使用Lambda表达式直接编写,大大减小了工做量
相关文章
相关标签/搜索