我们在开发中往往会遇到很多需要定时任务的请求,比如每天晚上凌晨去请求接口等。而且我们也不需要特别复杂的需求,比如需要什么分布式场景,这样我们可以用Spring的Scheduled。
下面的示例我用的是Springboot的项目
首先在启动类上添加@EnableScheduling 注解
先简单测试一个一个用例
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
@Component
public class RunIntervalTestScheduler {
@Scheduled(cron = "0/1 * * * * ?")
public void singleThreadTest1() {
log.info("singleThreadTest1");
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
}
@Scheduled(cron = "0/1 * * * * ?")
public void singleThreadTest2() {
log.info("singleThreadTest2");
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
}
@Scheduled(initialDelay = 1000, fixedRate = 100000)
public void singleThreadTest3() {
log.info("singleThreadTest3");
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
}
}
还需要添加一个配置类信息,目的是为了开启一个异步调度
@Configuration
public class ScheduledConfig implements SchedulingConfigurer {
private static final int TASK_POOL_SIZE = 50;
private static final String TASK_THREAD_PREFIX = "test-task-";
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
ThreadPoolTaskScheduler taskPool = new ThreadPoolTaskScheduler();
taskPool.setPoolSize(TASK_POOL_SIZE);
taskPool.setThreadNamePrefix(TASK_THREAD_PREFIX);
taskPool.initialize();
scheduledTaskRegistrar.setTaskScheduler(taskPool);
}
}
结果如下:
其中,@Scheduled注解支持cron表达式,fixedRate表示上一次的任务调度和下一次任务调度的间隔时间。
然而,我们在实际开发中,往往需要将这个定时实际通过参数传过去。而不是写在配置文件中。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.concurrent.ScheduledFuture;
@Controller
public class TestScheduler {
@Autowired
private ThreadPoolTaskScheduler threadPoolTaskScheduler;
@Bean
public ThreadPoolTaskScheduler threadPoolTaskScheduler(){
return new ThreadPoolTaskScheduler();
}
private ScheduledFuture> future;
@RequestMapping("/startCron")
@ResponseBody
public String startCron(@RequestParam("cron") Long cron){
System.out.println("x0");
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("2022-04-18 16:12:00");
future = threadPoolTaskScheduler.schedule(new Say("123"), new Date(System.currentTimeMillis()+10000));
System.out.println("x1");
return "x";
}
@RequestMapping("/stopCron")
@ResponseBody
public String stopCron(){
System.out.println("stop >>>>>");
if(future != null) {
future.cancel(true);
}
//future = threadPoolTaskScheduler.schedule(new Say(), new CronTrigger("*/5 * * * * *"));
System.out.println("stop <<<<<");
return "stop cron";
}
}
解读一下上面的代码:
startCron(Long cron)方法的参数cron需要接口的请求里传一个时间戳,我们将请求的参数cron转化为Date格式,调用threadPoolTaskScheduler.schedule,有人可能会有疑问,直接传cron表达式不好嘛?Scheduled支持的cron表达式默认是6位,不支持传年,所以如果请求参数传cron表达式,很有可能每年的这个时间都会执行一次。
调用接口ip:port/stopCron 会停止这个调度的执行
细心的小伙伴可能发现了这个new Say(“123”);这个是一个线程。
public class Say implements Runnable {
private String str;
Say(String str){
this.str = str;
}
@Override
public void run(){
System.out.println("" + new Date() + " hello" );
test(str);
}
void test(String str){
System.out.println("开始打印"+str);
}
}
我们可以传一个实际用的参数,调用test方法,test方法对应我们正常的业务代码,这里有一个点值得注意,那就是如果这个类Say里面需要其他的Service注入,那么只能以构造函数的方式,因为这个类上没有加@bean的注解,在Spring进行扫描的时候不会认为它是一个Bean。这个坑我之前就遇到过。
我们可以传一个实际用的参数,调用test方法,test方法对应我们正常的业务代码,这里有一个点值得注意,那就是如果这个类Say里面需要其他的Service注入,那么只能以构造函数的方式,因为这个类上没有加@bean的注解,在Spring进行扫描的时候不会认为它是一个Bean。这个坑我之前就遇到过。



