程序员潇然 发表于 2022-7-17 18:09:13

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

在Thread中有异常处理器相关的方法

!(data/attachment/forum/202207/17/180122kw14ewsqjh4w7eww.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

在ThreadGroup中也有相关的异常处理方法

!(data/attachment/forum/202207/17/180303eoozr6oqllomdq2z.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

### 示例

#### 未检查异常

!(data/attachment/forum/202207/17/180327iebuebi3aivu9i89.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

对于未检查异常,将会直接宕掉,主线程则继续运行,程序会继续运行
在主线程中能不能捕获呢?
我们简单粗暴一点,直接全部包到try catch中

!(data/attachment/forum/202207/17/180348rjbzovyxkvr00k0f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

你会发现,然而并没有什么卵用,主线程中的try catch并不会得到什么信息,跟原来的结果还是一样的,线程直接宕掉

#### 已检查异常

!(data/attachment/forum/202207/17/180410zjoz6uqa26uzaa8h.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

对于已检查的异常,run方法本身是不支持抛出的,上面代码中,想要throws,IDE提示异常,从run方法可以看得出来
run方法本身是不支持throws的(签名中没有throws)

!(data/attachment/forum/202207/17/180424ebbofabv26saav1v.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

所以怎么办?
既然是已检查异常,肯定是要处理的,既然不能丢出去,就只有一个办法了,那就是自己捕获,放置在try catch中

!(data/attachment/forum/202207/17/180437vagu2izamsta8zu2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

#### 小结

在run方法中是不能够抛出异常的,如果是已检查的异常,那么必须进行try catch

对于未检查的异常,如果没有进行处理,一旦抛出线程将会宕掉,而且在主线程中并不能捕获到这个异常

难道对于未检查的异常也都是try catch吗?(当然,这是一种方式)

还有没有其他解决方案?

### 异常处理器

在Java线程的run方法中,对于未检查异常,借助于异常处理器进行处理的

字面意思,直接理解为处理异常的方法,那么如何配置这个处理异常的方法呢?如何设置,又是如何调用?

UncaughtExceptionHandler,是Thread的内部接口(1.8中已经设置为函数式接口)

!(data/attachment/forum/202207/17/180501p0y0arm7r70390xl.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

Thread内部有两个变量,用于记录异常处理器

!(data/attachment/forum/202207/17/180520apqrup1l6rud6qd1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

对于两个set方法,没有什么特别的,主要就是设置这两个内部变量

!(data/attachment/forum/202207/17/180532sap5sng5656anssc.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

对于getUncaughtExceptionHandler方法,如果当前非空,那么返回当前,否则,将返回当前线程组,很显然,ThreadGroup实现了Thread.UncaughtExceptionHandler

!(data/attachment/forum/202207/17/180546siyn5nd5jz65dqkh.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

对于getDefaultUncaughtExceptionHandler,这是简单的返回内部变量

!(data/attachment/forum/202207/17/180603y2qf7mzqn7fuobns.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

此时我们大致了解到了这几个方法,内部有两个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调用

!(data/attachment/forum/202207/17/180628rolwl4blwbbe8e7w.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

所以关键点在于getUncaughtExceptionHandler返回什么异常处理器,我们再回过头来看下源代码

!(data/attachment/forum/202207/17/180637jfgifzfiii33cg54.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

`本文作者:程序员潇然 疯狂的字节X https://crazybytex.com/`

如果已经设置,那么将会直接返回;

如果没有设置,将会返回当前线程组(前面说了ThreadGroup实现了Thread.UncaughtExceptionHandler)

当调用ThreadGroup的uncaughtException方法时,如上图下半部分

如果他的父线程组重写了uncaughtException方法,那么将会调用他的父线程组的方法,如果父亲节点没有重写,爷爷节点重写了将会调用爷爷的,以此类推

但是如果所有的祖先线程组都没有重写呢?很显然,所有的方法代码都是上面这样子的(上图下半部分),将会递归到顶级线程组,然后不满足parent,然后走到else,这中间什么有意义的事情都没有做

在else中,会首先获取应用默认的异常处理器,如果仍旧是没有设置

不好意思,直接转到system.err了

!(data/attachment/forum/202207/17/180700sj5667920a64njfn.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

### 代码示例

!(data/attachment/forum/202207/17/180719gmaqycfhiacdhffr.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

从上面的示例可以看得出来,尽管仍就出现了异常,我们能够进行信息获取与感知,不会直接宕掉了

如果先start,然后在设置异常处理器会发生什么?

!(data/attachment/forum/202207/17/180732ftecbdribr29c5ws.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "image.png")

可以看得到,线程仍旧是直接宕掉,异常处理器无效,所以setUncaughtExceptionHandler方法必须在start方法前调用!

### 总结

在Thread中的run方法,不能够抛出异常,只能进行捕获
对于已检查异常,必须捕获
对于未检查异常,你也可以进行try catch,但是代码始终包裹在try中,真的好吗?
还另外提供了异常处理器机制用于处理未检查异常

有两种异常处理器:
线程自身的处理器和全局的异常处理器

1. 如果设置了异常处理器uncaughtExceptionHandler,那么将会使用这个
2. 如果没设置,将会在祖先线程组中查找第一个重写了uncaughtException的线程组,然后调用他的uncaughtException方法
3. 如果都没有重写,那么使用应用默认的全局异常处理器defaultUncaughtExceptionHandler
4. 如果还是没有设置,直接标准错误打印信息

如果想要设置自己的异常处理器,可以通过对应的setter方法进行设置,如果想要设置全局的可以调用静态方法进行设置

异常处理器Thread.UncaughtExceptionHandler是一个函数式接口,所以后续,你可以使用Lambda表达式直接编写,大大减少了工作量。

!(data/attachment/forum/202206/16/141330jha7st9soow8772i.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "common_log.png")
`转载务必注明出处:程序员潇然,疯狂的字节X,https://crazybytex.com/thread-65-1-1.html `

页: [1]
查看完整版本: 异常处理器详解 Java多线程异常处理机制 多线程中篇(四)