老板要求小省同学开发一个下班后提醒员工吃饭提醒的功能,谁料功能开发完成上线后,员工分分投诉说功能有问题,食堂都放饭一小时了,才发提醒到员工的手机上。这让身为干饭人的小省同学立刻意识到了问题的严重性,于是有了接下来的故事
上代码
@Scheduled(cron = "0/1 * * * * ? ")
public void punch() throws InterruptedException {
System.out.println("下班打卡提醒:" + DateUtils.dateTimeNow());
//模拟打卡提醒业务流程处理 如果延时一小时
Thread.sleep(1000 * 3);
}
@Scheduled(cron = "0/1 * * * * ? ")
public void eat() {
System.out.println("食堂开饭提醒:" + DateUtils.dateTimeNow());
}
通过代码发现,除了有员工吃饭的提醒定时任务,还有其他的定时任务也会执行,盲猜一波是其它定时任务影响到了吃饭提醒
下班打卡提醒:20220726182219
食堂开饭提醒:20220726182222
下班打卡提醒:20220726182223
食堂开饭提醒:20220726182226
下班打卡提醒:20220726182227
食堂开饭提醒:20220726182230
下班打卡提醒:20220726182231
代码运行代码发现,只要下班打卡执行后,食堂开饭就会出现延时,那么直接去掉下班打卡提醒不就OK了吗
食堂开饭提醒:20220726182511
食堂开饭提醒:20220726182512
食堂开饭提醒:20220726182513
食堂开饭提醒:20220726182514
食堂开饭提醒:20220726182515
食堂开饭提醒:20220726182516
食堂开饭提醒:20220726182517
去掉打卡提醒后,就完全正常了,好了 下班了
被老板狠狠的打了一顿后,发现下班打卡存在还是很有必要的
为什么会影响呢
老规划,打断点,查看源码,通过调用链排查到如下代码
@Override
public void run() {
try {
this.delegate.run();
}
catch (UndeclaredThrowableException ex) {
this.errorHandler.handleError(ex.getUndeclaredThrowable());
}
catch (Throwable ex) {
this.errorHandler.handleError(ex);
}
}
看到这里真相就水落石出了。
线程池中里面只有一个活跃线程在执行,那么在单线程的情况下,吃饭提醒就被其它定时任务阻塞了,所以出现了延时提醒
既然是因为单线程出现的问题,那么改成多线程或异步应该能解决问题了
先看一波官网咋搞
@Async
void doSomething() {
// this will be run asynchronously
}
加异步注解,确实可行,亲测可用
官网参考:
https://docs.spring.io/spring-framework/docs/current/reference/html/integration.html#scheduling-annotation-support-async
业务代码异步执行
public ThreadPoolTaskExecutor getAsyncExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(3);
taskExecutor.setMaxPoolSize(5);
taskExecutor.setQueueCapacity(200);
taskExecutor.setKeepAliveSeconds(300);
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
taskExecutor.setAwaitTerminationSeconds(60);
taskExecutor.initialize();
return taskExecutor;
}
@Scheduled(cron = "0/1 * * * * ? ")
public void punch() {
ThreadPoolTaskExecutor asyncExecutor = getAsyncExecutor();
asyncExecutor.execute(() -> {
System.out.println("下班打卡提醒:" + DateUtils.dateTimeNow());
//模拟打卡提醒业务流程处理
try {
Thread.sleep(1000 * 3);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
@Scheduled(cron = "0/1 * * * * ? ")
public void eat() {
System.out.println("食堂开饭提醒:" + DateUtils.dateTimeNow());
}
加入配置类
@Configuration
public class ScheduleConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(5);
taskRegistrar.setScheduler(scheduledExecutorService);
}
}
不以物喜,不以己悲。