1.业务中遇到一些场景: 做一个告警策略的生成的逻辑,需要对用户定义的告警信息进行循环判断,业务要求1000条要在15秒内处理完成.通过日志打印单条生成耗时0.08秒左右,粗略估算,单线程1秒能处理10条,如果有10个线程同时处理的话,那么理想状态10秒内是可以完成的;
在启动类中设置初始化线程,启动类 中使用@EnableAsync 开启异步支持
/** Set the ThreadPoolExecutor's core pool size. */
private int corePoolSize = 10;
/** Set the ThreadPoolExecutor's maximum pool size. */
private int maxPoolSize = 20;
/** Set the capacity for the ThreadPoolExecutor's BlockingQueue. */
private int queueCapacity = 200;
@Bean ("threadPoolTaskExecutor")
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setThreadNamePrefix("AlarmExecutor-");
executor.setKeepAliveSeconds(60);
// rejection-policy:当pool已经达到max size的时候,如何处理新任务
// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
/**
* 告警判断 与写入,通知逻辑
*
* @param alarmMessageInsertList
* @param alarmRevertMap
* @param updateCountList
*/
private void generateKpiAlarm(List alarmMessageInsertList, Map alarmRevertMap,
List updateCountList) {
// 3.0版本改造,将策略与资源一起查出
List alarmStrategies = alarmStMapper.selectAllAndSource();
if (NullUtil.isNull(alarmStrategies)) {
log.info("未查询到有效的告警策略!");
return;
}
for (AlarmStrategyVo alarmStrategy : alarmStrategies) {
//异步处理 单条判断->写入->通知
alarmGenerator.getAlarmNoticeMap(alarmStrategy);
}
@Async("threadPoolTaskExecutor")
@Override
public void getAlarmNoticeMap(AlarmStrategyVo alarmStrategy) {
//业务逻辑
}
for 循环里面的方法做了异步处理
这里遇到两个坑
1.初始化线程池的bean 一直失败 ,尝试新建类,将初始化线程池单独一个类,重启idea,重新编译,甚至新建一个项目都是可以正常使用的,然而在自己的项目无卵用
而正确的打印应该是 Initialzing ExeutorService
当天就是无论怎么都加载不进来,搞了一下午,关机下班,第二天一来 ,重新尝试,竟然ok了,判断可能是项目哪里冲突了,缓存原因,真实原因还仍未知,郁闷
2.第二个问题是for循环里的异步不生效,日志显示还是使用同步去跑的,异步没有生效
解决方法: 一开始for循环中为类中调用,查阅资料后将for循环里的方法单独做个类,异步才生效
3.不乱用注解,今天一个同事在不该使用多线程地方,乱用@Async 还使用了2秒一次的定时去跑任务,导致了很多bug,比如添加重复
原因:需要处理的常量池,多个线程在短时间内竞争,有的线程能拿到,有的线程拿不到资源,这里程序不会报错,但是业务上可能会有其他情况出现,导致后面的判断失误,这是没必要的.一个线程去处理就足够的,举个例子
线程10就没能成功取消任务,因为线程3已经获取到并提前取消了,后面的自然会有其他判断分歧,极短的时间循环,拿到的都数据也不是最新数据库修改成功的数据,可能3个线程同时获取到老数据,所以这三个的逻辑会同时添加成功,会有重复添加的情况.所以要考虑业务场景需不需要使用,而不是为了用而用;