Loading... Java定时任务的实现方式,一般是以下四种: `Timer`、`ScheduledExecutorService`、`Spring Task`、`Quartz` <!-- more--> ## Timer `Timer`: 这是java自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务。 使用这种方式可以让你的程序按照某一个频度执行,一般用的较少。 用Timer方式调用不支持多线程,且特定时间执行无效。 推荐使用ScheduledExecutorService替代Timer来调用。 ```java public class TimeTask { public static void main(String[] args) { java.util.TimerTask timerTask = new java.util.TimerTask() { @Override public void run() { System.out.println("TimeTask: " + System.currentTimeMillis()/1000); } }; // Timer方法不支持多线程,不推荐 // Timer timer = new Timer(); // timer.schedule(timerTask,0,3000); // 按时执行无效 // timer.schedule(timerTask,new Date(2021, Calendar.MARCH,22,9,56)); ScheduledExecutorService pool = new ScheduledThreadPoolExecutor(1); pool.scheduleAtFixedRate(timerTask,0,5, TimeUnit.SECONDS); } } ``` --- ## ScheduledExecutorService `ScheduledExecutorService`: 支持延后和定期执行任务。 基于线程池操作,任务并发执行。 只有当调度任务来的时候才真正执行任务,其它时间处于轮询任务状态。 ```java public class ScheduledExecutorServiceTask { public static void main(String[] args) { ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); executor.scheduleAtFixedRate(()->System.out.println("ScheduledExecutorServiceTask: " + System.currentTimeMillis()/1000), 0, 5, TimeUnit.SECONDS); } } ``` --- ## Spring Task `Spring Task`: Spring 3.0框架提供的计划任务,开发简单且执行效率高。 **但在计划任务数量太多的时候,可能出现阻塞,崩溃,延迟启动等问题。** 需要在此类中打上 `@Component`、`@Scheduled`注解以及在启动类中打上 `@EnableScheduling`注解。 @Scheduled核心属性是 `cron`,代表定时任务的触发计划表达式。这个表达式的格式为: `@Scheduled(cron="seconds minutes hours day month week year(可选)")` cron表达式中,每个位置的约束如下:  * `*`:可用在所有字段中,表示对应时间域的**每一个时刻**,例如,*在分钟字段时,表示“每分钟”; * `?`:该字符只在**日期**和**星期**字段中使用,它通常指定为“无意义的值”,相当于**占位符**; * `-`:表达一个**范围**,如在小时字段中使用“10-12”,则表示从10到12点,即10,11,12; * `,`:表达一个**列表值**,如在星期字段中使用“MON,WED,FRI”,则表示星期一,星期三和星期五; * `/`:x/y表达一个**等步长序列**,x为起始值,y为增量步长值。如在秒数字段中使用0/15,则表示为0,15,30和45秒,而5/15在分钟字段中表示5,20,35,50,你也可以使用*/y,它等同于0/y; * `#`:该字符只能在**星期**字段中使用,表示**当月某个工作日**。如6#3表示当月的第三个星期五(6表示星期五,#3表示当前的第三个),而4#5表示当月的第五个星期三,假设当月没有第五个星期三,忽略不触发; * `L`:该字符只在**日期**和**星期**字段中使用,代表“Last”的意思,但它在两个字段中意思不同。L在日期字段中,表示这个月份的最后一天,如一月的31号,非闰年二月的28号;如果L用在星期中,则表示星期六,等同于7。但是,如果L出现在星期字段里,而且在前面有一个数值X,则表示“这个月的最后X天”,例如,6L表示该月的最后星期五; * `W`:该字符只能出现在**日期**字段里,是对前导日期的修饰,表示**离该日期最近的工作日**。例如15W表示离该月15号最近的工作日,如果该月15号是星期六,则匹配14号星期五;如果15日是星期日,则匹配16号星期一;如果15号是星期二,那结果就是15号星期二。但必须注意关联的匹配日期不能够跨月,如你指定1W,如果1号是星期六,结果匹配的是3号星期一,而非上个月最后的那天。W字符串只能指定单一日期,而不能指定日期范围; * `LW`:在**日期**字段可以组合使用LW,它的意思是**当月的最后一个工作日**; * `C`:该字符只在**日期**和**星期**字段中使用,代表“Calendar”的意思。它的意思是**计划所关联的日期**,如果日期没有被关联,则相当于日历中所有日期。例如5C在日期字段中就相当于日历5日以后的第一天。1C在星期字段中相当于星期日后的第一天。 除了cron,还有两个常见的属性是 `fixedRate`和 `fixedDelay`,他们和 `cron`的区别如下代码: ```java @Component public class SpringTask { private static final Logger logger = LoggerFactory.getLogger(SpringTask.class); private static int flag = 1; /** * @Scheduled(cron="seconds minutes hours day month week year(可选)") * cron:每5秒执行一次,但是睡眠了8秒。也就是说当第二次定时任务要启动的时候被阻塞了,只能推到下一个周期 * 也就是[0 10 20 30] */ @Scheduled(cron = "0/5 * * * * *") public void scheduled() throws InterruptedException { logger.error("=====>>>>>使用cron--" + flag++ + " {}",System.currentTimeMillis()/1000); Thread.sleep(8000); } /** * fixedRate:每5秒为一个周期,如果执行时间超过5s则执行结束之后立马接着执行下一个,后面所有的任务都延迟了。 * 也就是[0 6 12 18 24 30] */ @Scheduled(fixedRate = 5000) public void scheduled1() throws InterruptedException { logger.error("=====>>>>>使用fixedRate--" + flag++ + " {}", System.currentTimeMillis()/1000); Thread.sleep(6000); } /** * fixedDelay:不管任务执行多久,执行完后总是停5秒再执行第二次 * 也就是第二次执行的时间为3s执行时长+5s等待=8s:[0 8 16 24 32 40] */ @Scheduled(fixedDelay = 5000) public void scheduled2() throws InterruptedException { logger.error("=====>>>>>fixedDelay--" + flag++ + " {}",System.currentTimeMillis()/1000); Thread.sleep(3000); } } ``` --- ## Quartz `Quartz`: 基于SpringBoot 2.0,是完全有java编写的开源调度框架,需要引入依赖 org.quartz-scheduler,并需要在启动类打上 `@EnableScheduling`注解。 只需要定义 Job(任务)、Trigger(触发器)和Scheduler(调度器) 就可以实现定时调度功能。 它适合运用于比Spring Task更为复杂的定时任务系统中,并广泛运用于分布式上。 依赖: ```xml <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.2</version> </dependency> ``` 执行内容: ```java public class QuartzTask extends QuartzJobBean { private static final Logger logger = LoggerFactory.getLogger(QuartzTask.class); private static int flag = 1; @Override protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException { logger.error("QuartzTask--" + flag++ + ":{}",System.currentTimeMillis()/1000); } } ``` 环境初始化: ```java @Configuration public class QuartzConfig { /** * 创建Job对象(应该会默认交给Spring容器进行管理) */ @Bean public JobDetail teatQuartzDetail(){ return JobBuilder.newJob(QuartzTask.class).withIdentity("QuartzTask").storeDurably().build(); } /** * 管理Trigger对象,相当于管理一个cron表达式 */ @Bean public Trigger testQuartzTrigger(){ SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(5) .repeatForever(); return TriggerBuilder.newTrigger().forJob(teatQuartzDetail()) .withIdentity("QuartzTask") .withSchedule(scheduleBuilder) .build(); } } ``` 还有一种方案是基于分布式的配置,传送门:[SpringBoot定时任务(schedule、quartz)](https://www.cnblogs.com/jing99/p/11546559.html) 现在主流的框架/脚手架用的也都是这一套,有空可以根据若依或者是人人开源的脚手架学习一下。 Last modification:August 22, 2022 © Allow specification reprint Like 0 喵ฅฅ
One comment
偷学 可耻