栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

记一次使用Spring注解@Scheduled出现的事故

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

记一次使用Spring注解@Scheduled出现的事故

问题

老板要求小省同学开发一个下班后提醒员工吃饭提醒的功能,谁料功能开发完成上线后,员工分分投诉说功能有问题,食堂都放饭一小时了,才发提醒到员工的手机上。这让身为干饭人的小省同学立刻意识到了问题的严重性,于是有了接下来的故事

原因

上代码

 @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);
    }
}

不以物喜,不以己悲。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1027335.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号