JDK线程池的拒绝策略

Posted zhaiyt

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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类。


 [zhai1]调用接续接口更新来话原因方法

 [zhai2]线程池的拒绝策略,是指当任务添加到线程池中被拒绝,而采取的处理措施。当任务添加到线程池中之所以被拒绝,可能是由于:第一,线程池异常关闭。第二,任务数量超过线程池的最大限制

以上是关于JDK线程池的拒绝策略的主要内容,如果未能解决你的问题,请参考以下文章

jdk线程池的四种拒绝策略

第224天学习打卡(知识点回顾 线程池的拒绝策略)

Java并发多线程编程——线程池

Java线程池的拒绝策略

线程池浅学

线程池的拒绝策略示例