又是喜闻乐见的生产事故环节,不过这次是隔壁项目组的,看了一眼事故原因大概就比较清楚了
大致情况是在线程池的操作中,没有添加异常策略,导致采用了默认的线程池拒绝策略抛出异常,影响了程序的执行
参数简单介绍一下
public
- corePoolSize 线程池中的核心线程数,不受运行时间限制,只要线程池在运行就会保持这个线程的运行
- maximumPoolSize 线程池最大线程数
- keepAliveTime 存活时间,超出核心线程数的线程在超出这个时间后会进行回收
- TimeUnit 存活时间的单位
- workQueue 执行队列,在任务进入到线程池的线程中执行前,会放到这个队列中,
- threadFactory 用来创建线程的工厂方法,里面也只有一个new Thread的接口方法,可以对加入线程池的线程进行统一处理比如命名等,如下所示
public
可以看到,线程名称已经做了调整
- handler:最后就是这次问题的原因,因为线程池中如果不添加拒绝策略的构造器,采用的是默认的拒绝策略,直接抛出异常
public
这个defaultHandler其实就是AbortPolicy
public
直接就报错了,在隔壁业务逻辑下是显然不合理的,可以看一下线程池的执行逻辑
public
在添加到队列失败的时候会进行reject方法,然后调用拒绝策略的rejectedExecution方法,有以下几个策略方案
CallerRunsPolicy
AbortPolicy 是直接抛出异常的已经说过了,看一下第二个CallerRunsPolicy,该策略方案为,在当前线程池未关闭的情况下,直接在当前线程Runnable中执行调用
public
比如上文中的线程池参数调整一下,限制队列长度为3,采用该策略执行进行执行
ThreadPoolExecutor
发生了一件很魔法的事,明明是顺序队列,但是打印出来的结果却发生了变化,不仅顺序颠倒,而且TestThreadFactory的命名也发生了变化,就是因为后来的线程在当前线程”插队”导致的
DiscardOldestPolicy
该方法会在线程塞满队列后踢出最早的线程e.getQueue().poll()并将当前任务添加进队列
public
DiscardPolicy
这个就直接无视掉溢出的任务了。。。目前隔壁业务组采用的这个方案,我觉得是不太合理的。。因为会导致关联的业务获取数据不准确
public
RejectedExecutionHandler
其实我认为如果业务逻辑比较独特的话,自定义是比较合理的拒绝策略,上文也可以看出所有的拒绝策略都是实现了RejectedExecutionHandler这个接口来进行的,对我们而言,如果是必要的情况下,可以适当在拒绝策略里进行逻辑调整,比如放到缓存,或者数据库,然后通过定时任务的情况来进行更新,如下
public
我们在执行时,如果出现该情况,会进行自定义拒绝策略的方案
给个别同事吱了一声。。希望隔壁项目组数据不会丢太多