@Scheduled多线程执行定时任务
@Scheduled 这个注解给我们带了很大的方便,我们只要加上该注解,并且根据需求设置好就可以使用定时任务了。
但是,我们需要注意的是,@Scheduled 并不一定一定会按时执行。
关于cron表达式详解可以看这里:https://www.jianshu.com/p/1defb0f22ed1
是不是懵了?我们看看代码
//定时任务1,每秒运行一次
@Scheduled(cron = "0/1 * * * * ?")
public void autoRun() {
Date date = new Date();
Date oneMinLater = new Date(date.getTime() + 60 * 1000);
// runJob(()->{//分布式锁
log.info("begin-------自动执行形象进度日记录");
Date date1 = new Date();
//模拟在做一个耗时很长的事情。
while (oneMinLater.getTime() > date1.getTime()) {
date1 = new Date();
}
log.info("end-------自动执行形象进度日记录完成");
// },"形象进度");
}
//定时任务2,每5秒运行一次
@Scheduled(cron = "0/5 * * * * ? ")
public void checkBimModelTreeBuildTimeout() {
String desc = "检查构建树超时情况";
// runJob(()->{
log.info("begin-------{}" ,desc);
log.info("end-------{}" ,desc);
// }, desc);
}
结果:
15:38:17.001 [ozxThreadPool-1-thread-1] INFO c.b.b.p.job.GraphicProgressJob - begin-------自动执行形象进度日记录
而我们预期的结果:
15:40:07.001 [ozxThreadPool-1-thread-1] INFO c.b.b.p.job.GraphicProgressJob - begin-------自动执行形象进度日记录
15:40:10.001 [ozxThreadPool-1-thread-2] INFO c.b.b.p.j.BimModelTreeBuildTimeoutJob - begin-------检查构建树超时情况
15:40:10.001 [ozxThreadPool-1-thread-2] INFO c.b.b.p.j.BimModelTreeBuildTimeoutJob - end-------检查构建树超时情况
15:40:15.002 [ozxThreadPool-1-thread-4] INFO c.b.b.p.j.BimModelTreeBuildTimeoutJob - begin-------检查构建树超时情况
15:40:15.002 [ozxThreadPool-1-thread-4] INFO c.b.b.p.j.BimModelTreeBuildTimeoutJob - end-------检查构建树超时情况
所以定时任务2并没有执行
因为使用@Scheduled 的定时任务虽然是异步执行的,但是,不同的定时任务之间并不是并行的!!!!!!!!
在其中一个定时任务没有执行完之前,其他的定时任务即使是到了执行时间,也是不会执行的,它们会进行排队。
也就是如果你想你不同的定时任务互不影响,到时间就会执行,那么你最好将你的定时任务方法自己搞成异步方法,这样,
定时任务其实就相当于调用了一个线程执行任务,一瞬间就结束了。当然,也可以勉强当做是任务都会定时执行。
加上下面这个配置类就可以使定时任务变成异步的了
@Configuration
public class ScheduleConfig implements SchedulingConfigurer {
@Value("${aib.threadPool.configList[1].name:schedule}")
private String schedule;
public static final String DEFAULT_SCHEDULE_POOL_NAME = "schedule";
public static final Integer COREPOOLSIZE = 8;
public static final Integer MAXIMUMPOOLSIZE = COREPOOLSIZE * 2;
public static final Integer WAITQUEUECAPACITY = 100;
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
ThreadTaskPool taskPool = ThreadTaskPools.getInstance().getTaskPool(schedule);
if (null == taskPool) {
// return;
ThreadPoolConfig threadPoolConfig = new ThreadPoolConfig("ozxThreadPool", ThreadTaskPoolType.SCHEDULE, COREPOOLSIZE,MAXIMUMPOOLSIZE,WAITQUEUECAPACITY);
taskPool = new ThreadTaskPool(threadPoolConfig);
}
ThreadPoolExecutor threadPoolExecutor = taskPool.getThreadPoolExecutor();
//创建scheduledExecutorService线程池
// ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(8, new PoolThreadFactory("ozxThreadPool"));
//解决ScheduledExecutorService最大线程数Integer.MAX_VALUE问题
// ((ScheduledThreadPoolExecutor) scheduledExecutorService).setMaximumPoolSize(16);
scheduledTaskRegistrar.setScheduler(threadPoolExecutor);
}
}
ps:【大家都说不要用Executors的各种构造方法去实例化线程池。其实主要的问题是newScheduledThreadPool的构造方法中是这么写的】
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), threadFactory);//因为Integer.MAX_VALUE,所以可能会造成OOM
}
我的理解是只要修改最大线程数就好了。((ScheduledThreadPoolExecutor) scheduledExecutorService).setMaximumPoolSize(16);
说明:Executors各个方法的弊端:
1)newFixedThreadPool和newSingleThreadExecutor:
主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
2)newCachedThreadPool和newScheduledThreadPool:
主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。