JDK线程池的拒绝策略
关于新疆服务请求未带入来话原因的问题
经核查,该问题是由于立单接口内部没有成功调用接续的 “更新来电原因接口”导致的,接续测更新来电原因接口编码:NGCCT_UPDATESRFLAG_PUT ,立单接口调用代码如下:
final Map<String, Object> paramsMap = outputObject.getBean(); paramsMap.put("provCode", provCode); paramsMap.put("tenantId", MapUtils.getString(inputObject.getParams(), "tenantId")); TaskEngine.getInstance().submit(new Runnable() { @Override public void run() { callContactRel(paramsMap);[zhai1] backWriteWrkfm(paramsMap); } });
立单接口中使用起多线程异步调用方式,更新来电原因方法内部记录日志,由于线程内部日志无法在火眼系统查看,当出现该问题时,
1.多次找在线运维人员查看主机上的日志,均未发现更新来话原因方法内部记录的日志;
2.登陆csf平台查询该接口的调用记录,根据出现问题的流水号均未查到相关调用记录;
3.核查调用更新来电原因方法内部catch中如表的错误记录,也未发现问题;
根据火眼上上下文日志,确定程序一定运行到了启动多线程的地方,遂怀疑线程池的问题,
该处启用多线程使用的是 TaskEngine工具提供的获取线程池,使用池中资源执行任务。
经核查TaskEngine类发现该类在初始化的时候会创建一个线程池,核心线程数为1,最大线程数为30的线程池,代码如下:
private static TaskEngine instance = new TaskEngine(); public static TaskEngine getInstance() { return instance; } private TaskEngine() { executor = new ThreadPoolExecutor(1, 30,180L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new ThreadFactory() { final AtomicInteger threadNumber = new AtomicInteger(1); public Thread newThread(Runnable runnable) { // Use our own naming scheme for the threads. Thread thread = new Thread(Thread.currentThread().getThreadGroup(), runnable, "TaskEngine-pool-" + threadNumber.getAndIncrement(), 0); // Make workers daemon threads. thread.setDaemon(true); if (thread.getPriority() != Thread.NORM_PRIORITY) { thread.setPriority(Thread.NORM_PRIORITY); } return thread; } }); }
该线程池构造时尚未指明 拒绝策略 [zhai2] 因此会默认使用 AbortPolicy
JDK提供的ThreadPoolExecutor 线程池有四种 拒绝策略:
AbortPolicy 当任务添加到线程池中被拒绝时,它将抛出 RejectedExecutionException 异常。
CallerRunsPolicy 当任务添加到线程池中被拒绝时,会在线程池当前正在运行的Thread线程池中处理被拒绝的任务。
DiscardOldestPolicy 当任务添加到线程池中被拒绝时,线程池会放弃等待队列中最旧的未处理任务,然后将被拒绝的任务添加到等待队列中。
DiscardPolicy 当任务添加到线程池中被拒绝时,线程池将丢弃被拒绝的任务。
工程中使用的默认的策略,会不会存在一种情况,在某一个时刻,使用TaskEngine类获取线程池来执行任务时,30个线程同时被使用,导致创建任务数大于最大线程数的限制,该任务将无法被成功执行。
Demo验证:
1.使用与项目中相同的线程池执行数据入库操作,模拟设置最大线程数10,核心线程1个,拒绝策略默认:
@RequestMapping(value = "/testThread") public String testThread(){ final CommonCfgCode commonCfgCode = new CommonCfgCode(); commonCfgCode.setCodeTypeCd("Thread"); TaskEngine taskEngine = TaskEngine.getInstance(); for(int i=0;i<100;i++){ taskEngine.submit(new Runnable() { @Override public void run() { testService.save(commonCfgCode); } }); } return "SUCCESS"; }
循环执行100个任务进行如表操作抛出:
java.util.concurrent.RejectedExecutionException异常,与该策略描述一致。
查看如表数据:仅如表10条数据;
2.将拒绝策略修改为:CallerRunsPolicy 其它不变;
无报错,查询数据:如表100条;(110条中包含使用默认拒绝抛异常策略如表的10条)
Demo git路径:https://git.lug.ustc.edu.cn/zhaiyt/mylife.git
由于该问题仅在新疆出现,出现的场景无法预知,请各位评审是否可以修改项目中TaskEngine类。