@Shcedule默认情况下会单线程顺序执行。如果一个定时任务执行时间大于其任务间隔时间,那么下一次将会等待上一次执行结束后再继续执行。如果多个定时任务在同一时刻执行,任务会依次执行。
1. 单线程顺序执行demo:
package com.citi.ark.mls.timer; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; @Service @Slf4j //@EnableAsync public class ExecuteSequence { @Scheduled(cron = "0/3 * * * * ?") // @Async public void test1() throws InterruptedException { Long current=System.currentTimeMillis(); log.info("===this is test1 start:{}===",current); Thread.sleep(9900); log.info("===this is test1 end:{}===",System.currentTimeMillis()-current); // log.info("==test1 output:{}",i++); } @Scheduled(cron = "0/3 * * * * ?") // @Async public void test2() throws InterruptedException { Long current=System.currentTimeMillis(); log.info("===this is test2 start:{}===",current); Thread.sleep(5000); log.info("===this is test2 end:{}===",System.currentTimeMillis()-current); // log.info("==test2 output:{}",i++); } private Integer getResult(int i){ int j=i++; return j; } }
执行结果如下,可见是单线程顺序执行
2022-07-22 18:08:27.013 c.c.a.m.t.ExecuteSequence scheduling-1 [INFO] ===this is test1 start:1658484507013===
2022-07-22 18:08:37.027 c.c.a.m.t.ExecuteSequence scheduling-1 [INFO] ===this is test1 end:10014===
2022-07-22 18:08:37.029 c.c.a.m.t.ExecuteSequence scheduling-1 [INFO] ===this is test2 start:1658484517028===
2022-07-22 18:08:42.037 c.c.a.m.t.ExecuteSequence scheduling-1 [INFO] ===this is test2 end:5009===
2022-07-22 18:08:42.039 c.c.a.m.t.ExecuteSequence scheduling-1 [INFO] ===this is test1 start:1658484522038===
2022-07-22 18:08:52.041 c.c.a.m.t.ExecuteSequence scheduling-1 [INFO] ===this is test1 end:10003===
2. 异步方式处理——利用Spring提供的@Async注解和@EnableAsync注解
demo 1 中效率会比较低
package mls.timer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
@Service
@Slf4j
@EnableAsync
public class ExecuteSequence {
@Scheduled(cron = "0/3 * * * * ?")
@Async
public void test1() throws InterruptedException {
Long current=System.currentTimeMillis();
log.info("===this is test1 start:{}===",current);
Thread.sleep(10000);
log.info("===this is test1 end:{}===",System.currentTimeMillis()-current);
// log.info("==test1 output:{}",i++);
}
@Scheduled(cron = "0/3 * * * * ?")
@Async
public void test2() throws InterruptedException {
Long current=System.currentTimeMillis();
log.info("===this is test2 start:{}===",current);
Thread.sleep(5000);
log.info("===this is test2 end:{}===",System.currentTimeMillis()-current);
// log.info("==test2 output:{}",i++);
}
private Integer getResult(int i){
int j=i++;
return j;
}
}
执行结果如下,由于test1/test2执行时间是5s>任务调度周期3s,发生上一个任务未执行完毕下一个任务又开始执行逻辑,从如下执行结果可以明显可出。所以最好执行方式是采用多线程自己控制线程数据量
2022-07-25 11:38:18.030 c.c.a.m.t.ExecuteSequence task-1 [INFO] ===this is test2 start:1658720298030===
2022-07-25 11:38:18.030 c.c.a.m.t.ExecuteSequence task-2 [INFO] ===this is test1 start:1658720298030=== //
2022-07-25 11:38:21.007 c.c.a.m.t.ExecuteSequence task-3 [INFO] ===this is test1 start:1658720301007=== //上一个test1还没end下一个test1又开始了
2022-07-25 11:38:21.007 c.c.a.m.t.ExecuteSequence task-4 [INFO] ===this is test2 start:1658720301007===
2022-07-25 11:38:23.036 c.c.a.m.t.ExecuteSequence task-1 [INFO] ===this is test2 end:5006===
2022-07-25 11:38:24.015 c.c.a.m.t.ExecuteSequence task-5 [INFO] ===this is test2 start:1658720304015===
2022-07-25 11:38:24.016 c.c.a.m.t.ExecuteSequence task-6 [INFO] ===this is test1 start:1658720304016===
2022-07-25 11:38:26.021 c.c.a.m.t.ExecuteSequence task-4 [INFO] ===this is test2 end:5014===
2022-07-25 11:38:27.002 c.c.a.m.t.ExecuteSequence task-7 [INFO] ===this is test2 start:1658720307002===
2022-07-25 11:38:27.003 c.c.a.m.t.ExecuteSequence task-8 [INFO] ===this is test1 start:1658720307003===
2022-07-25 11:38:28.037 c.c.a.m.t.ExecuteSequence task-2 [INFO] ===this is test1 end:10007===
2022-07-25 11:38:29.025 c.c.a.m.t.ExecuteSequence task-5 [INFO] ===this is test2 end:5010===
2022-07-25 11:38:30.011 c.c.a.m.t.ExecuteSequence task-1 [INFO] ===this is test1 start:1658720310011===
2022-07-25 11:38:30.011 c.c.a.m.t.ExecuteSequence task-4 [INFO] ===this is test2 start:1658720310011===
2022-07-25 11:38:31.012 c.c.a.m.t.ExecuteSequence task-3 [INFO] ===this is test1 end:10005===
2022-07-25 11:38:32.013 c.c.a.m.t.ExecuteSequence task-7 [INFO] ===this is test2 end:5011===
2022-07-25 11:38:33.003 c.c.a.m.t.ExecuteSequence task-2 [INFO] ===this is test1 start:1658720313003===
2022-07-25 11:38:33.003 c.c.a.m.t.ExecuteSequence task-5 [INFO] ===this is test2 start:1658720313003===
2022-07-25 11:38:34.025 c.c.a.m.t.ExecuteSequence task-6 [INFO] ===this is test1 end:10009===
2022-07-25 11:38:35.015 c.c.a.m.t.ExecuteSequence task-4 [INFO] ===this is test2 end:5004===
2022-07-25 11:38:36.012 c.c.a.m.t.ExecuteSequence task-3 [INFO] ===this is test1 start:1658720316012===
2022-07-25 11:38:36.012 c.c.a.m.t.ExecuteSequence task-7 [INFO] ===this is test2 start:1658720316012===
2022-07-25 11:38:37.008 c.c.a.m.t.ExecuteSequence task-8 [INFO] ===this is test1 end:10005===
3. 多线程执行 -- 自定义线程池 @EnableScheduling
demo2采用@Async 和@EnableAsync方式,每次定时任务启动都会创建一个单独的线程来处理,也就是说同一个定时任务(比如下例中test1)也会启动多个线程处理
首先需要实现SchedulingConfigurer接口,然后自定义线程池,这样凡是用到@Scheduled注解的都可以用该线程池
package com.citi.ark.mls.timer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@Configuration
@EnableScheduling
public class ScheduleConfig implements SchedulingConfigurer{
@Bean
public Executor executors() {
return Executors.newScheduledThreadPool(5,(Runnable r) ->{
Thread thread=new Thread(r);
thread.setName("ScheduleThread");
return thread;
});
}
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
scheduledTaskRegistrar.setScheduler(executors());
}
}
package com.citi.ark.mls.timer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
@Service
@Slf4j
public class ExecuteSequence {
@Scheduled(cron = "0/3 * * * * ?")
@Async
public void test1() throws InterruptedException {
Long current=System.currentTimeMillis();
log.info("===this is test1 start:{}===",current);
Thread.sleep(10000);
log.info("===this is test1 end:{}===",System.currentTimeMillis()-current);
// log.info("==test1 output:{}",i++);
}
@Scheduled(cron = "0/3 * * * * ?")
@Async
public void test2() throws InterruptedException {
Long current=System.currentTimeMillis();
log.info("===this is test2 start:{}===",current);
Thread.sleep(5000);
log.info("===this is test2 end:{}===",System.currentTimeMillis()-current);
// log.info("==test2 output:{}",i++);
}
private Integer getResult(int i){
int j=i++;
return j;
}
}
执行结果如下,可以看到test2在thread3未执行完之前并不会执行下一个test2任务,即解决了同一个任务启动多个线程处理问题
2022-07-25 11:32:48.008 c.c.a.m.t.ExecuteSequence Thread-3 [INFO] ===this is test2 start:1658719968008===
2022-07-25 11:32:48.008 c.c.a.m.t.ExecuteSequence Thread-4 [INFO] ===this is test1 start:1658719968008===
2022-07-25 11:32:53.010 c.c.a.m.t.ExecuteSequence Thread-3 [INFO] ===this is test2 end:5002===
2022-07-25 11:32:54.006 c.c.a.m.t.ExecuteSequence Thread-3 [INFO] ===this is test2 start:1658719974006===
2022-07-25 11:32:58.016 c.c.a.m.t.ExecuteSequence Thread-4 [INFO] ===this is test1 end:10008===
2022-07-25 11:32:59.019 c.c.a.m.t.ExecuteSequence Thread-3 [INFO] ===this is test2 end:5013===
2022-07-25 11:33:00.000 c.c.a.m.t.ExecuteSequence Thread-4 [INFO] ===this is test1 start:1658719980000===
2022-07-25 11:33:00.016 c.c.a.m.t.ExecuteSequence Thread-6 [INFO] ===this is test2 start:1658719980016===
2022-07-25 11:33:05.029 c.c.a.m.t.ExecuteSequence Thread-6 [INFO] ===this is test2 end:5013===
2022-07-25 11:33:06.012 c.c.a.m.t.ExecuteSequence Thread-6 [INFO] ===this is test2 start:1658719986012===
2022-07-25 11:33:10.015 c.c.a.m.t.ExecuteSequence Thread-4 [INFO] ===this is test1 end:10015===
2022-07-25 11:33:11.020 c.c.a.m.t.ExecuteSequence Thread-6 [INFO] ===this is test2 end:5008===
2022-07-25 11:33:12.007 c.c.a.m.t.ExecuteSequence Thread-4 [INFO] ===this is test1 start:1658719992007===
2022-07-25 11:33:12.007 c.c.a.m.t.ExecuteSequence Thread-5 [INFO] ===this is test2 start:1658719992007===
2022-07-25 11:33:17.012 c.c.a.m.t.ExecuteSequence Thread-5 [INFO] ===this is test2 end:5004===
2022-07-25 11:33:18.002 c.c.a.m.t.ExecuteSequence Thread-5 [INFO] ===this is test2 start:1658719998002===
2022-07-25 11:33:22.022 c.c.a.m.t.ExecuteSequence Thread-4 [INFO] ===this is test1 end:10015===
2022-07-25 11:33:23.011 c.c.a.m.t.ExecuteSequence Thread-5 [INFO] ===this is test2 end:5009===
参考:
https://www.jb51.net/article/226985.htm
https://blog.csdn.net/weixin_44117767/article/details/119996464