一次线程池异常捕获失败问题

背景:

利用springboot+mysql做一个伪mq,当项目启动后,调用写好的方法每隔30秒获取一次数据,然后利用线程池多线程消费,出异常后递归此方法,形成一个监听服务。

核心代码:

1.项目启动调用查询,此处特备注意下这个接口(InitializingBean)

public class MimidaiOperatorReportApplication  implements InitializingBean {
    private Logger logger = LoggerFactory.getLogger(MimidaiOperatorReportApplication.class);
    @Autowired
    private SjmhOpdReportGetController sjmhOpdReportGetController;
    public static void main(String[] args) {
        SpringApplication.run(MimidaiOperatorReportApplication.class, args);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        String resultNew = sjmhOpdReportGetController.getSjmhReportNew();
        logger.info("SjmhGetReportNewTheResultIs:{}",resultNew);
    }
}

2.利用线程池消费,每执行一次程序计数器减1

while(true){
    for (SjmhReportTask sjmhReportTask : taskList) {
    threadPoolForReportTask.execute(new Runnable() {
        @Override
        public void run() {
            try {
               //执行业务代码
               /*
               省略n行代码
               **/
               insert(student);
            } catch (Exception e) {
                logger.error("用户userId:{},申请Id:{},upcId:{}请求出错{}", sjmhReportTask.getUserId(),
                        sjmhReportTask.getApplyId(), sjmhReportTask.getUpcId(), e.getMessage(), e);
            } finally {
                taskCount.countDown();
            }
        }
    });
   }
}

 注意:此处insert(student)中student每个字段不能为空。

3.循环监听剩余任务数

while (true) {
    if (whileTimes > 6) {
        logger.info("=======循环次数已经超过最大值,结束循环======");
        setTaskCount(0);
    }
    Thread.sleep(3000);
    logger.info("剩余在跑的任务为:{}条", this.taskCount.getCount());
    if (this.taskCount.getCount() < 1) {
        //跑完之后释放系统内存
        taskList.clear();
        break;
    }
    whileTimes++;
}

 

问题:

刚开始的时候跑的都很正常,直到有一天,我发现日志中一直打 剩余在跑的任务为:1条,等到循环6次之后跳出循环,这时候查看日志并没有发现什么异常日志,然后开始查看数据,发现有一个表的这条数据没有插入,最后再从日志中抓到这个记录对应的信息,发现student有一个字段为空。导致插入失败。不知 为何异常没有打出来,而且这个线程直接死掉,导致没有执行finally中countdown()。因此日志中一直出现 剩余在跑的任务为:1条的日志。

分析:

1.单元测试。

将这段有问题的代码拿出来执行单元测试,发现异常都能正常输出。

2.一下输出异常信息太多,导致线程卡死

经过多次测试,无此问题。

3.此异常无法捕捉(DataIntegrityViolationException)

手动抛出此异常,而且其他异常均能正常捕获。

网上各种尝试均未能解决,懵逼ing...

契机:

在多次尝试中,发现有一次这个异常在主线程中打了出来,仿佛看到希望。于是突然想起excute抛出异常的时候,有时候会依赖于父线程,我项目启动的时候实现了InitializingBean接口,afterPropertiesSet之中调用我业务方法,此时突然发现当主线程未执行结束的时候,子线程已经开始执行,而就在此时,执行的子线程中有空值,出异常的子线程开始检测是否有父线程,父线程也进入while (true) {}死循环中,因此将异常抛给父线程,当主线程此时已经没有执行权,最终导致异常无法输出。

解决方法:

在整个项目启动后,即主线程执行结束后,开始任务处理,这样父线程就没有了,每个子线程就会调用默认的异常处理hanlder处理异常。

代码如下:

@Component
public class OpdGetReportStarter implements ApplicationRunner {
    private Logger logger = LoggerFactory.getLogger(OpdGetReportStarter.class);
    @Autowired
    private SjmhOpdReportGetController sjmhOpdReportGetController;
    @Override
    public void run(ApplicationArguments args) throws Exception {
        String resultNew = sjmhOpdReportGetController.getSjmhReportNew();
        logger.info("获取结果:{}" ,resultNew);
    }
}

 

你可能感兴趣的:(java,java,线程池异常捕获失败)