添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接


Java中有那些定时任务的实现方式,下面从java原生和spring封装对动态管理定时任务的实现简单的聊一聊,不涉及外部组件的实现方式。

java 单线程定时运行 java定时任务只执行一次_java

1、Timer

java.util包下提供了对定时任务的支持,涉及2个类:

  • Timer:定时器类
  • TimerTask:任务抽象类

使用该定时任务我们需要继承TimerTask抽象类,覆盖run方法编写任务执行代码,并利用Timer定时器对TimerTask进行调度。

java 单线程定时运行 java定时任务只执行一次_Timer_02

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,它的每个调度任务都会分配到线程池中的一个线程去执行,并发不受影响,各自执行各自的。

java 单线程定时运行 java定时任务只执行一次_spring_03


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可以在构造函数中指定多个对应的后台线程数。

java 单线程定时运行 java定时任务只执行一次_定时任务_04


ThreadPoolExecutor构造方法及其作用:

java 单线程定时运行 java定时任务只执行一次_java 单线程定时运行_05

  • corePoolSize:线程池维护线程的最少数量
  • maximumPoolSize:线程池维护线程的最大数量
  • keepAliveTime: 线程池维护线程所允许的空闲时间
  • unit: 线程池维护线程所允许的空闲时间的单位
  • workQueue: 线程池所使用的缓冲队列
  • handler: 线程池对拒绝任务的处理策略

ScheduledThreadPoolExecutor构造方法及其作用:

java 单线程定时运行 java定时任务只执行一次_定时任务_06


实例见第2小节,ScheduledThreadPoolExecutor是接口ScheduledExecutorService的实现类。

4、ThreadPoolTaskExecutor和ThreadPoolTaskScheduler

springboot自动装配的两个常用线程池对象:ThreadPoolTaskExecutor,ThreadPoolTaskScheduler(参考org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration),分别对应jdk的两个线程池,是静态代理增强,故ThreadPoolTaskScheduler的api是最丰富的。

  • ThreadPoolTaskExecutor:一个专门用于执行任务的类
  • java 单线程定时运行 java定时任务只执行一次_Timer_07

  • ThreadPoolTaskScheduler:一个专门用于调度任务的类
  • java 单线程定时运行 java定时任务只执行一次_Timer_08

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();
}

在请求端点的方法中调用创建定时任务方法即可,结果类似如下

java 单线程定时运行 java定时任务只执行一次_Timer_09