从Runnable中的运行时异常说起

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 前段时间,夜晚突然收到报警,紧急上线排查。由于dba操作不当,大片数据回滚,发生锁表的情况,请求返回时间过长,使得系统打印出大量的RejectedExecutionException的异常。定位到代码片段类似: Java代码 ThreadPoolExecutor workers = 
前段时间,夜晚突然收到报警,紧急上线排查。由于dba操作不当,大片数据回滚,发生锁表的情况,请求返回时间过长,使得系统打印出大量的RejectedExecutionException的异常。定位到代码片段类似:
Java代码


这里就要说说ThreadPoolExecutor和ArrayBlockingQueue了,众所周知ArrayBlockingQueue类是一个阻塞的队列。当和ThreadPoolExecutor使用时,ThreadPoolExecutor会在初始化时开启corePoolSize(也就是上面代码中的10)个线程,去消费队列里的task。当并发量增大,直到corePoolSize全都在执行task,而队列也放满了待处理的task的时候,ThreadPoolExecutor就会去创建一个新的线程。直至达到线程池中的消费线程达到maximumPoolSize(也就是上面代码里的600)。如果这个时候再有task加入,根据默认的饱和策略,将会抛出RejectedExecutionException异常。

这次就是由于获取数据库等待时间超长,导致task响应时间变慢,继而线程池中活跃的消费线程堆积到600个线程依然无法应付,才抛出的RejectedExecutionException。

显然抛出RejectedExecutionException不是那么的友好,我们在这里可以自定义饱和策略。默认系统饱和策略是抛出异常。

Java代码



另外当线程故障恢复时,通过日志惊奇的观察到,队列中的线程以串行的方式运行了一短时间,不过很快就正常了。于是在本地写了一个测试。

Java代码



运行如上的代码,打印日志如下:

Java代码




可以看到,最后的10个task是以类似串行的方式在运行。

这里的原因在于ThreadPoolExecutor的中断策略,当Runnable中抛出RTE时,ThreadPoolExecutor会将执行当前的Runnable的线程Dead。由于例子的代码100%会抛出RTE,最终的结果就是ThreadPoolExecutor中存活的消费线程数变为0。ThreadPoolExecutor创建线程只有在初始化,调用excute等几个主动方法中才会去做,我们任务的提交早在一开始就已经做了,最终导致的结果就是永远只存在一个线程服务这个任务队列。

那么要比较优雅的解决这个问题,可以使用FutureTask,FutureTask里面会处理运行时异常,不会将其抛出给ThreadPoolExecutor。

相关文章
|
20天前
|
监控 Java
解析Java线程池的异常处理机制
该内容是一个关于Java线程和线程池异常处理的总结。提到的关键点包括: 1. 引用了滑动验证页面和相关文章资源。 2. 区分了`execute`与`submit`在处理线程异常时的区别,`submit`可能会捕获并隐藏异常,而`execute`会直接抛出。 3. 提供了处理线程和线程池异常的建议,如使用try/catch直接捕获,或者自定义线程工厂和未捕获异常处理器。 4. 示例代码展示了如何通过设置`UncaughtExceptionHandler`来监控和处理线程中的异常。 请注意,由于字符限制,这里只提供了简要摘要,详细解释和代码示例请参考原文。
21 3
|
2天前
|
Java
面试官:小伙子来说一说Java中final关键字,以及它和finally、finalize()有什么区别?
面试官:“小伙子,用过final关键字吗?” 我:“必须用过呀” 面试官:“好,那来说一说你对这个关键字的理解吧,再说一说它与finally、finalize()的区别” 我:“好嘞!
16 1
|
8月前
|
Java 调度
Java多线程(Thread,Runnable,Callable)附带相关面试题
1.通过继承Thread类实现多线程,2.多线程常用操作方法,3.通过Runnable接口实现多线程,4.通过Lambda与Thread结合实现快速创建多线程,5.通过实现Callable接口得到线程返回值
105 0
|
9月前
|
Java Linux C++
java心法线程篇(1)——线程等待方法的异同
java心法线程篇(1)——线程等待方法的异同
72 1
|
Java 开发者 容器
【Java挠头】Java异常、捕获、处理、throw、throws等绝妙剖析
【Java挠头】Java异常、捕获、处理、throw、throws等绝妙剖析
126 0
【Java挠头】Java异常、捕获、处理、throw、throws等绝妙剖析
|
安全 Java 编译器
[Java基础面试题一]深入谈谈final、finally、 finalize 有什么不同?吊打面试官
[Java基础面试题一]深入谈谈final、finally、 finalize 有什么不同?吊打面试官
|
消息中间件 监控 Java
【小家Java】一次Java线程池误用(newFixedThreadPool)引发的线上血案和总结(上)
【小家Java】一次Java线程池误用(newFixedThreadPool)引发的线上血案和总结(上)
【小家Java】一次Java线程池误用(newFixedThreadPool)引发的线上血案和总结(上)
|
缓存 Java
【小家Java】一次Java线程池误用(newFixedThreadPool)引发的线上血案和总结(下)
【小家Java】一次Java线程池误用(newFixedThreadPool)引发的线上血案和总结(下)
|
Java API 调度
JDK源码解析之AbstractQueuedSynchronizer(下)
JDK源码解析之AbstractQueuedSynchronizer(下)
82 0
JDK源码解析之AbstractQueuedSynchronizer(下)
|
存储 安全 Java
JDK源码解析之AbstractQueuedSynchronizer(上)
JDK源码解析之AbstractQueuedSynchronizer(上)
81 0
JDK源码解析之AbstractQueuedSynchronizer(上)