java定时任务
使用Spring Task
我百度之后有三种实现方法,我是直接通过Spring 提供的 @Scheduled 注解即可定义定时任务,保证方法每月调用一次:
@Scheduled(cron=”0 59 23 28-31 * ?”)
关于cron:cron表达式格式:{秒} {分钟} {小时} {日期} {月份} {星期} {年份(可为空)}
注:
“*”:代表每隔1秒钟触发
“,”:代表在指定的秒数触发,例如,”1,5,20”表示1秒,5秒和20秒时触发
“-“:代表在指定的范围内触发,例如,”15-20”表示从15秒开始触发到20秒结束触发,每隔1秒触发1次。
“/“:代表指定数值的增量触发,例如,在秒域中使用”10/15”表示每分钟内第10秒触发,后续每隔15秒触发一次。注意:这种间隔循环只在每分钟内执行,所以在第55秒执行一次后,中断每15秒的间隔循环,等到下一分钟的第10秒再次开始执行。
【注】由于{日期}和{星期}互斥,所以其中之一被定值以后,必须对另一个设”?”
Spring Task 底层是基于 JDK 的 ScheduledThreadPoolExecutor 线程池来实现的。
以Spring Boot来作为示例,步骤为
在启动类所在包下创建Schedule 类(在没有配置@ComponentScan的情况下,Spring Boot只会默认扫描启动类所在包的spring组件)
在该类上添加@Component和@EnableScheduling注解
在方法上添加@Scheduled注解,该注解主要参数如下
String cron() default “”; // 支持cron表达式
long fixedDelay() default -1; // 在最后一次调用结束和下一次调用开始之间的时间间隔,以毫秒为单位
String fixedDelayString() default “”; // 同上,类似ScheduledExecutorService的scheduleWithFixedDelay
long fixedRate() default -1; // 在调用之前的时间间隔,以毫秒为单位
String fixedRateString() default “”; // 同上,类似ScheduledExecutorService的scheduleAtFixedRate
long initialDelay() default -1; // 在第一次执行fixedRate()或fixedDelay()任务之前要延迟的毫秒数
String initialDelayString() default “”; // 同上
代码示例:
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
@Component
@EnableScheduling
public class Schedule {@Scheduled(fixedRate = 2000L)
public void task() {System.out.println(“当前线程:” + Thread.currentThread().getName() + “ 当前时间” + LocalDateTime.now());
}
}
其他方法是:
使用java.util.Timer
这种方式的定时任务主要用到两个类,Timer 和 TimerTask,使用起来比较简单。其中 Timer 负责设定 TimerTask 的起始与间隔执行时间。
TimerTask是一个抽象类,new的时候实现自己的 run 方法,然后将其丢给 Timer 去执行即可。
代码示例:
import java.time.LocalDateTime;
import java.util.Timer;
import java.util.TimerTask;
public class Schedule {
public static void main(String[] args) {
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
System.out.println(“当前线程:” + Thread.currentThread().getName() + “ 当前时间” + LocalDateTime.now());
}
};
// 在指定延迟0毫秒后开始,随后地执行以2000毫秒间隔执行timerTask
new Timer().schedule(timerTask, 0L, 2000L);
System.out.println(“当前线程:” + Thread.currentThread().getName() + “ 当前时间” + LocalDateTime.now());
}
}
缺点:
Timer 的背后只有一个线程,不管有多少个任务,都只有一个工作线程串行执行,效率低下
受限于单线程,如果第一个任务逻辑上死循环了,后续的任务一个都得不到执行
依然是由于单线程,任一任务抛出异常后,整个 Timer 就会结束,后续任务全部都无法执行
使用ScheduledExecutorService
ScheduledExecutorService 即是 Timer 的替代者,JDK 1.5 并发包引入,是基于线程池设计的定时任务类。
每个调度任务都会分配到线程池中的某一个线程去执行,任务就是并发调度执行的,任务之间互不影响。
代码示例:
import java.time.LocalDateTime;
import java.util.concurrent.*;
public class Schedule {
public static void main(String[] args) {
// 创建一个ScheduledThreadPoolExecutor线程池,核心线程数为5
ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(5);
// 创建Runnable打印当前线程和当前时间
Runnable r = () -> System.out.println(“当前线程:” + Thread.currentThread().getName() + “ 当前时间” + LocalDateTime.now());
/**
* schedule:只执行一次调度
* scheduleAtFixedRate:一开始就计算间隔时间,如果任务超过间隔时间,那么就直接开始下一个任务
* scheduleWithFixedDelay:任务无论执行多久,都要等待上一轮任务完成之后再间隔指定时间,然后才开始下一个任务
*/
// 在指定1秒延迟后执行r,之后每两秒执行一次
scheduledExecutorService.scheduleAtFixedRate(r, 1, 2, TimeUnit.SECONDS);
}
}