第一种方式: spring boot 中引用
main 函数引入:@EnableScheduling
package org.test.springboot;
import org.springframework.boot.*;
import org.springframework.boot.autoconfigure.*;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
public class Launcher {
public static void main(String[] args) {
SpringApplication.run(Launcher.class, args);
}
}
package org.test.springboot.service;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.text.SimpleDateFormat;
import java.util.Date;
@Service
public class ScheduledService {
@Scheduled(cron = "*/5 * * * * *")
public void hello(){
System.out.println("hello...");
}
@Scheduled(fixedRate = 5000)
public void fixedRate(){
System.out.println("fixedRate: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
}
}
-
fixedRate:定义一个按一定频率执行的定时任务
-
fixedDelay:定义一个按一定频率执行的定时任务,与上面不同的是,该属性可以配合
initialDelay
, 定义该任务延迟执行时间。 -
cron:通过表达式来配置任务执行时间
@Scheduled中的参数说明
`@Scheduled(fixedRate=2000):上一次开始执行时间点后2秒再次执行;` `@Scheduled(fixedDelay=2000):上一次执行完毕时间点后2秒再次执行;` `@Scheduled(initialDelay=1000, fixedDelay=2000):第一次延迟1秒执行,然后在上一次执行完毕时间点后2秒再次执行;` `@Scheduled(cron=``"* * * * * ?"``):按cron规则执行。`
延伸学习:
可以看到2个定时任务都已经执行,并且使同一个线程中串行执行,如果只有一个定时任务,这样做肯定没问题,当定时任务增多,如果一个任务卡死,会导致其他任务也无法执行。
多线程执行
在传统的Spring项目中,我们可以在xml配置文件添加task的配置,而在SpringBoot项目中一般使用config配置类的方式添加配置,所以新建一个AsyncConfig类
package org.test.springboot;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
@Configuration
@EnableAsync
public class AsyncConfig {
/*
此处成员变量应该使用@Value从配置中读取
*/
private int corePoolSize = 10;
private int maxPoolSize = 200;
private int queueCapacity = 10;
@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.initialize();
return executor;
}
}
@Configuration
:表明该类是一个配置类
@EnableAsync
:开启异步事件的支持
然后在定时任务的类或者方法上添加@Async
。最后重启项目,每一个任务都是在不同的线程中
例如:
package org.test.springboot.service;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.text.SimpleDateFormat;
import java.util.Date;
@Service
public class ScheduledService {
@Async
@Scheduled(cron = "*/5 * * * * *")
public void hello(){
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("hello...");
}
@Async
@Scheduled(fixedRate = 2000)
public void fixedRate(){
System.out.println("fixedRate: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
}
}
第二种方式 : 使用:Timer
package org.test.springboot;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class Launcher {
public static void main(String[] args) {
//SpringApplication.run(Launcher.class, args);
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
System.out.println("task run:"+ new Date());
}
};
Timer timer = new Timer();
//安排指定的任务在指定的时间开始进行重复的固定延迟执行。这里是每3秒执行一次
timer.schedule(timerTask,10,3000);
}
}
第三种方法:使用ScheduledExecutorService
package org.test.springboot;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Launcher {
public static void main(String[] args) {
ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
// 参数:1、任务体
// 2、首次执行的延时时间
// 3、任务执行间隔
// 4、间隔时间单位
service.scheduleAtFixedRate(()->System.out.println("task ScheduledExecutorService "+new Date()), 0, 3, TimeUnit.SECONDS);
}
}
cron表达式
注:下面内容主要参考Quartz的文档,对于Spring Task基本都适用。
cron表达式是由7个域组成的字符串,它们描述了任务计划的各个细节,这些域用空格分隔,每个域代表的含义如下:
Seconds(秒)
Minutes(分)
Hours(时)
Day-of-Month(日)
Month(月)
Day-of-Week(星期)
Year(可选字段)(年)
示例:0 0 10 ? * WED表示每个星期三的10:00:00
表达式 {秒} {分} {时} {日} {月} {周} {年}(可选)
允许值 0~59 0~59 0~23 1~31 1~12
JAN~DEC 1~7
SUN~SAT 1970~2099
特殊值 , - * / , - * / , - * / , - * /
? L W C , - * / , - * /
? L C # , - * /
说明:下面描述中,XX域则表示cron表达式相应的位置,如秒域表示cron中第1个值,日域则表示cron表达式第4个值等等。
月份简称:JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV和DEC。
星期简称:SUN,MON,TUE,WED,THU,FRI和SAT。其中,1表示SUN。
,:用来分割在域上指定的多个值。如:MON,WED,FRI在星期域里表示星期一、星期三、星期五。
/:用于指定增量值。如分钟上使用0/15,表示从零开始,每隔15分钟,等价于0,15,30,45。如分钟上使用3/15,表示从第3分钟开始,每隔15分钟,等价于3,18,33,48,x/y中x表示开始值,y表示步长。
*:表示匹配该域的任意值。如秒上使用*表示每秒触发一次。
-:表示指定一个范围,如分钟域上10-13,表示10分、11分、12分、13分。
?:表示不关心的域,可用于日和周两个域上,主要用来解决日和周两个域的冲突。和*类似,区别在于*关心域,只是域的值可以任意,?则表示对该域不关心,不需要看该域的内容,直接忽略。
L:表示最后,是单词last的首字母,可用于日和周两个域上,用在日和周上含义不同:
日域上表示月份中日期的最后一天,如一月的第31天、非闰年二月的第28天。
周域上单独使用仅表示7或SAT,即仅表示周六。但是如果跟在其他值后,如6L或FRIL则表示该月中最后一个星期五。
L还可以指定偏移量,如日域指定L-3,表示该月倒数第3天。当使用L时其值尽量不要指定列表或范围,以免令人困惑。
W:用于日域,表示距离指定日最近的星期几(周一至周五中的一个),如:日域上值为15W则表示距离本月第15日最近的工作日。
# :用于周域,表示该月的第n个星期几。如:周域值为6#3或FRI#3表示该月的第3个星期五。
四、常用表达式示例
0 0 10,14,16 * * ?每天上午10点、下午两点、下午4点整触发
0 0/30 9-17 * * ? 每天朝九晚五内每隔半小时触发
0 15 10 ? * MON-FRI 周一至周五的上午10:15触发
0 0/5 * * * ?每5分钟触发
10 0/5 * * * ?每隔5分钟的第10秒触发(即10:00:10、10:05:10、10:10:10等)
30 * * * * ? 每半分钟触发
30 10 * * * ? 每小时的10分30秒触发
30 10 1 * * ? 每天1点10分30秒触发
30 10 1 20 * ? 每月20号1点10分30秒触发
30 10 1 20 10 ? * 每年10月20号1点10分30秒触发
30 10 1 20 10 ? 2011 2011年10月20号1点10分30秒触发
30 10 1 ? 10 * 2011 2011年10月每天1点10分30秒触发
30 10 1 ? 10 SUN 2011 2011年10月每周日1点10分30秒触发
15,30,45 * * * * ? 每15秒,30秒,45秒时触发
15-45 * * * * ? 15到45秒内,每秒都触发
15/5 * * * * ? 每分钟的每15秒开始触发,每隔5秒触发一次
15-30/5 * * * * ? 每分钟的15秒到30秒之间开始触发,每隔5秒触发一次
0 0/3 * * * ? 每小时的第0分0秒开始,每三分钟触发一次
0 15 10 ? * MON-FRI 星期一到星期五的10点15分0秒触发
0 15 10 L * ? 每个月最后一天的10点15分0秒触发
0 15 10 LW * ? 每个月最后一个工作日的10点15分0秒触发
0 15 10 ? * 5L 每个月最后一个星期四的10点15分0秒触发
0 15 10 ? * 5#3 每个月第三周的星期四的10点15分0秒触发
网友评论