注解方式
单线程
1、@EnableScheduling
在启动类添加注解
@SpringBootApplication
@EnableScheduling
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
2、@Scheduled
在定时执行的方法之上添加注解(类上需要有@Component注解)
@Component
public class Task1 {
@Scheduled(cron ="*/1 * * * * ?")
public void sayWord() {
System.out.println("world");
}
}
多线程
1、@EnableAsync+@EnableScheduling
在启动类添加注解
@SpringBootApplication
@EnableScheduling
@EnableAsync // 开启多线程
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
2、@Async+@Scheduled
在定时执行的方法之上添加注解(类上需要有@Component注解)
@Component
public class Task1 {
@Async
@Scheduled(cron ="*/1 * * * * ?")
public void sayWord() {
System.out.println("world");
}
}
@Scheduled参数
cron
语法格式:"秒域 分域 时域 日域 月域 周域 年域"(一般是六个或七个字段)
取值范围
域名 | 可取值 | 可取符号(仅列部分常用) |
秒域 | 0~59的整数 | * - , / |
分域 | 0~59的整数 | * - , / |
时域 | 0~23的整数 | * - , / |
日域 | 1~31的整数 | * - , / ? L |
月域 | 1~12的整数或JAN~DEC | * - , / |
周域 | 1~7的整数或SUN~SAT | * - , / ? L # |
年域 | 1970~2099的整数 | * - , / |
在线生成corn表达式网址:在线Cron表达式生成器
fixedDelay
指定两次任务执行的时间间隔(毫秒),前一个任务结束与下一个任务开始的间隔
如:@Scheduled(fixedDelay = 5*1000 ),表示第一个任务执行结束,开始计时,过5秒后,开始第二次执行。
fixedRate
指定两次任务执行的时间间隔(毫秒),前一个任务开始与下一个任务开始的间隔。
如:@Scheduled(fixedRate= 5*1000 ),表示第一个任务开始执行,开始计时,过5秒后,开始第二次执行。
initialDelay
第一次延迟执行的时间
如:@Scheduled(initialDelay=1000, fixedRate=5000) //第一次延迟1秒后执行,之后按fixedRate的规则每5秒执行一次
动态定时任务
1、@EnableScheduling
在启动类必须加上@EnableScheduling注解
@SpringBootApplication
@EnableScheduling
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
2、SchedulingConfigurer
实现SchedulingConfigurer并重写configureTasks方法
@Component
@EnableScheduling
public class MyTask implements SchedulingConfigurer {
@Autowired
protected CronMapper cronMapper;
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
scheduledTaskRegistrar.addTriggerTask(() -> process(),
triggerContext -> {
String cron = cronMapper.getCron(1);
if (cron.isEmpty()) {
System.out.println("cron is null");
}
return new CronTrigger(cron).nextExecutionTime(triggerContext);
});
}
// 定时任务
private void process() {
//TODO
System.out.println("执行动态定时任务:"+new Date(System.currentTimeMillis()));
}
}
TaskScheduler接口
任务调度接口 TaskScheduler,它提供了多种方法来调度将来某个时间点要运行的任务。
schedule(Runnable task, Trigger trigger);
指定一个触发器执行定时任务。可以使用CronTrigger来指定Cron表达式,执行定时任务。schedule(Runnable task, Date startTime);
指定一个具体时间点执行定时任务,可以动态的指定时间,开启任务。只执行一次。scheduleAtFixedRate(Runnable task, long period);
立即执行,循环任务,指定一个执行周期(毫秒计时)
不管上一个周期是否执行完,到时间下个周期就开始执行scheduleAtFixedRate(Runnable task, Date startTime, long period);
指定时间开始执行,循环任务,指定一个间隔周期(毫秒计时)
不管上一个周期是否执行完,到时间下个周期就开始执行scheduleWithFixedDelay(Runnable task, long delay);
立即执行,循环任务,指定一个间隔周期(毫秒计时)
上一个周期执行完,等待delay时间,下个周期开始执行scheduleWithFixedDelay(Runnable task, Date startTime, long delay);
指定时间开始执行,循环任务,指定一个间隔周期(毫秒计时)
上一个周期执行完,等待delay时间,下个周期开始执行
TaskScheduler有4个实现类: ConcurrentTaskScheduler、DefaultManagedTaskScheduler、ThreadPoolTaskScheduler、TimerManagerTaskScheduler。
ConcurrentTaskScheduler
以当前线程执行任务,单个线程方式执行定时任务,适用于简单场景。
示例:
ConcurrentTaskScheduler taskScheduler = new ConcurrentTaskScheduler();
// 执行一次
taskScheduler.execute(() -> log.info(Thread.currentThread().getName() + "执行一次"));
// 周期性执行
taskScheduler.schedule(() -> log.info(Thread.currentThread().getName() + "多次执行"), new CronTrigger("0/2 * * * * ?"));
DefaultManagedTaskScheduler
以当前线程执行任务,这是ConcurrentTaskScheduler的子类,添加了JNDI的支持。和ConcurrentTaskScheduler一样的用法,需要使用JNDI可以单独设置。
ThreadPoolTaskScheduler
提供线程池管理的调度器:多线程定时任务执行,实现了TaskExecutor接口,从而使的单一的实例可以尽可能快地异步执行。可以设置执行线程池数(默认一个线程)。
ThreadPoolTaskScheduler 的方法:
- 使用前必须得先调用initialize()【初始化方法】
- 有shutDown()方法,可以关闭线程池
setPoolSize
设置线程池大小,最小为1,默认情况下也为1;
setErrorHandler
设置异常处理器。
getScheduledThreadPoolExecutor
获取ScheduledExecutor,默认是ScheduledThreadPoolExecutor类型。
getActiveCount
获取当前活动的线程数
execute
提交执行一次的任务
submit\submitListenable
提交执行一次的任务,并且返回一个Future对象供判断任务状态使用。
方法1:增加配置类
@Configuration
public class ScheduleConfig {
@Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setPoolSize(5);
taskScheduler.initialize();
return taskScheduler;
}
}
方法2:在执行类里运行下面代码
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setPoolSize(6);
taskScheduler.initialize(); // 务必调用此方法来手动启动
执行方法:
// 执行一次
taskScheduler.execute(new Runnable() {
@Override
public void run() {
log.info(Thread.currentThread().getName() + "执行一次");
}
});
// 周期性执行
taskScheduler.schedule((new Runnable() {
@Override
public void run() {
log.info(Thread.currentThread().getName() + "多次执行");
}
}), new CronTrigger("0/2 * * * * ?"));
TimerManagerTaskScheduler
同时继承CommonJ中的TimerManager接口。在使用CommonJ进行调度时使用。
停止
ScheduledFuture:通过cancle和读取isCancelled结果来退出
调用完成之后会返回一个scheduledFuture,这个就是当前的任务调度器,停止的时候需要找到这个调度器,用这个调用器来终止。
boolean cancelled = scheduledFuture.isCancelled(); // 用来判断是否已经取消
if(!cancelled ){
scheduledFuture.cancel(true);// 用来将当前的任务取消
}
异常
1、No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to e xpose the current request.
没有找到与线程绑定的请求:你是指在实际的web请求之外引用请求属性,还是在最初接收请求的线程之外处理请求?如果你确实在web请求中操作,但仍然收到这个消息,那么你的代码可能在DispatcherServlet之外运行:在这种情况下,使用RequestContextListener或RequestContextFilter来暴露当前请求。
因为这个错误信息是与Spring框架相关的,通常发生在尝试在实际的web请求之外访问请求属性,或者在最初接收请求的线程之外处理请求时。如果你正在处理一个web请求但仍然收到这个错误信息,那么很可能是因为你的代码在DispatcherServlet之外运行。DispatcherServlet负责处理web请求并在Spring框架中管理请求的生命周期。
问题发生的位置:
((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest()
修改为
try{
HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
}catch (IllegalStateException e){
return null;
}
2、可以结合事务
@Scheduled(cron = "0 0/1 * * * ? ")
@Transactional(rollbackFor = Exception.class)
public void addDept() {
。。。
}