Java中有那些定时任务的实现方式,下面从java原生和spring封装对动态管理定时任务的实现简单的聊一聊,不涉及外部组件的实现方式。
1、Timer
java.util包下提供了对定时任务的支持,涉及2个类:
-
Timer:定时器类
-
TimerTask:任务抽象类
使用该定时任务我们需要继承TimerTask抽象类,覆盖run方法编写任务执行代码,并利用Timer定时器对TimerTask进行调度。
Timer提供了多种方法,可分为一次性任务和可重复执行任务。
1.1、一次性任务
一次性任务是指Timer执行一次之后,该任务后续不再执行,包括2个方法:
-
void schedule(TimerTask task, long delay):延迟delay毫秒后执行一次task
-
void schedule(TimerTask task, Date time):在指定时间time执行一次task,如果time过期,将会立即执行
1.2、可重复执行任务
可重复执行任务是指任务允许按照设定的规则重复执行,包括4个方法:
-
void schedule(TimerTask task, long delay, long period):延迟delay毫秒后执行task,之后每隔period毫秒执行一次task
-
void schedule(TimerTask task, Date firstTime, long period):在指定时间time执行一次task,之后每隔period毫秒执行一次task
-
void scheduleAtFixedRate(TimerTask task, long delay, long period):延迟delay毫秒后执行task,之后每隔period毫秒执行一次task
-
void scheduleAtFixedRate(TimerTask task, Date firstTime, long period):在指定时间time执行一次task,之后每隔period毫秒执行一次task
1.3、实例
public class DownloadTimerTask extends TimerTask {
@Override
public void run() {
System.out.println("指定时间的任务操作内容");
}
public static void main(String[] args) throws ParseException {
// Timer执行周期任务基于绝对时间,会受到系统时间的影响
Timer timer = new Timer();
TimerTask downloadTimerTask = new DownloadTimerTask();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
Date date = simpleDateFormat.parse("2023-03-21 10:16:00");
timer.schedule(downloadTimerTask,date);
}
在idea中会看到”使用ScheduledExecutorService代替Timer吧 “提示,可见并不推荐使用Timer来实现定时任务。
2、ScheduledExecutorService
java.util.concurrent包下提供了基于线程池设计的定时任务类ScheduledExecutorService,它的每个调度任务都会分配到线程池中的一个线程去执行,并发不受影响,各自执行各自的。
ScheduledExecutorService提供了3种方法:
-
schedule:只执行一次调度,方法第一个參数是任务实例,第二个參数是延迟时间,第三个是时间单元
-
scheduleAtFixedRate:一开始就计算间隔时间,如果任务超过间隔时间,那么就直接开始下一个任务
-
scheduleWithFixedDelay:任务无论执行多久,都要等待上一轮任务完成之后再间隔指定时间,然后才开始下一个任务
实例:
public static void main(String[] args) {
// ScheduledThreadPoolExecutor基于相对时间,不会受到系统时间的影响
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
//ScheduledExecutorService scheduledExecutorServicePool = new ScheduledThreadPoolExecutor(5, r -> new Thread(r, "executor-service-pool-" + r.hashCode()));
TimerTask downloadTimerTask = new DownloadTimerTask();
// 延迟5min后,运行且仅仅运行一次task
scheduledExecutorService.schedule(downloadTimerTask, 5, TimeUnit.MINUTES);
}
3、ThreadPoolExecutor和ScheduledThreadPoolExecutor
jdk原生的两个常用线程池对象:ThreadPoolExecutor、ScheduledThreadPoolExecutor。
ScheduledThreadPoolExecutor继承自ThreadPoolExecutor,主要用来在给定的延迟之后运行任务,或者定期执行任务。ScheduledThreadPoolExecutor的功能与Timer类似,但ScheduledThreadPoolExecutor功能更强大、更灵活。Timer对应的是单个后台线程,而 ScheduledThreadPoolExecutor可以在构造函数中指定多个对应的后台线程数。
ThreadPoolExecutor构造方法及其作用:
-
corePoolSize:线程池维护线程的最少数量
-
maximumPoolSize:线程池维护线程的最大数量
-
keepAliveTime: 线程池维护线程所允许的空闲时间
-
unit: 线程池维护线程所允许的空闲时间的单位
-
workQueue: 线程池所使用的缓冲队列
-
handler: 线程池对拒绝任务的处理策略
ScheduledThreadPoolExecutor构造方法及其作用:
实例见第2小节,ScheduledThreadPoolExecutor是接口ScheduledExecutorService的实现类。
4、ThreadPoolTaskExecutor和ThreadPoolTaskScheduler
springboot自动装配的两个常用线程池对象:ThreadPoolTaskExecutor,ThreadPoolTaskScheduler(参考org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration),分别对应jdk的两个线程池,是静态代理增强,故ThreadPoolTaskScheduler的api是最丰富的。
-
-
ThreadPoolTaskExecutor:一个专门用于执行任务的类
-
-
ThreadPoolTaskScheduler:一个专门用于调度任务的类
ThreadPoolTaskScheduler是spring中提供的线程池任务调度类,能够开启线程池进行任务调度。
ThreadPoolTaskScheduler 提供了4种定时任务方法:
-
schedule(Runnable task, Date stateTime),在指定时间执行一次定时任务
-
schedule(Runnable task, Trigger trigger),动态创建指定表达式cron的定时任务threadPoolTaskScheduler.schedule(() -> {}, triggerContext -> newCronTrigger(“”).nextExecutionTime(triggerContext));
-
scheduleAtFixedRate,指定间隔时间执行一次任务,间隔时间为前一次执行开始到下次任务开始时间
-
scheduleWithFixedDelay,指定间隔时间执行一次任务,间隔时间为前一次任务完成到下一次开始时间
ThreadPoolTaskScheduler.schedule()方法会创建一个定时计划ScheduledFuture,在这个方法需要添加两个参数,Runnable(线程接口类) 和CronTrigger(定时任务触发器),在ScheduledFuture中有一个cancel可以停止定时任务。
实例:
@Configuration
public class ThreadPoolTaskSchedulerConfig {
@Bean
public ThreadPoolTaskScheduler syncScheduler() {
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(5);
threadPoolTaskScheduler.setRemoveOnCancelPolicy(true);
threadPoolTaskScheduler.setThreadNamePrefix("syncThread-");
return threadPoolTaskScheduler;
}
@Slf4j
public abstract class AbstractJob implements Runnable {
private ScheduledFuture<?> scheduledFuture;
@Autowired
private ThreadPoolTaskScheduler threadPoolTaskScheduler;
@Override
public void run() {
begin();
execute();
end();
* 定时任务执行之前
public void begin() {
log.info("------开始执行任务------");
* 执行任务
public abstract void execute();
* 定时任务执行之后
public void end() {
log.info("------结束执行任务------");
* @description: 创建定时任务
* @param: cron 时间表达式
* @return: void
public void createJob(String cron) {
try {
log.info("创建定时任务");
scheduledFuture = threadPoolTaskScheduler.schedule(this, new CronTrigger(cron));
} catch (Exception e) {
log.error("创建定时任务失败!");
e.printStackTrace();
* 取消执行任务
public void cancelJob() {
try {
if (scheduledFuture != null) {
scheduledFuture.cancel(true);
log.info("取消执行任务!");
} catch (Exception e) {
log.error("取消执行失败 !");
e.printStackTrace();
}
在请求端点的方法中调用创建定时任务方法即可,结果类似如下