ScheduledExecutorService

895 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第25天,点击查看活动详情

1. ScheduledExecutorService 认识

1.1 介绍

ScheduledExecutorService 是 Java 1.5 版本新增的定时任务接口,基于线程池的方式,每个任务会开启一个新的线程执行,互不影响。

ScheduledExecutorService 存在于 java.util.concurrent 包中,其出现主要是针对使用 Timer 定时器的问题。

如果在定时任务中使用了 Timer ,热情的 IDEA 编译器还为主动提供新方案的使用方法:

// 使用 ScheduledExecutorService代替Timer吧  
// Inspection info: 处理定时任务时,Timer 运行多个 TimeTask 时,只要其中之一没有捕获抛出的异常,其它任务便会自动终止运行,使用 ScheduledExecutorService 则没有这个问题。

1.2 使用

  1. ScheduledExecutorService 可以使用实现的线程池类 ScheduledThreadPoolExecutor 来创建一个具体的对象。
ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1, new BasicThreadFactory.Builder().namingPattern("example-schedule-pool-%d").daemon(true).build());
executorService.scheduleAtFixedRate(new Runnable() {
    @Override
    public void run() {
        //do something
    }
},initialDelay,period, TimeUnit.HOURS);
  1. 除此之外,juc 包中还封装了 Executors 方法类便于快速创建对象,如果不需要自定义相关参数,可以使用 Executors 来初始化。
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();

2. ScheduledExecutorService 结构

2.1 接口定义

ScheduledExecutorService 作为一个接口,其继承了 ExecutorService 接口,并且提供了调度命令来延迟或者周期性的执行线程任务。

public interface ScheduledExecutorService extends ExecutorService {
    ...
}
  • 继承了 ExecutorService 接口,则 ExecutorService 其中定义的方法 ScheduledExecutorService 都可以使用

  • 因为 ScheduledExecutorService 是一个接口,因此在创建其对象并调用方法时需要使用其实现类:

    • ScheduledThreadPoolExecutor 类,用于创建 ScheduledExecutorService 类型对象,其也继承了线程池创建类,创建时需要指定核心线程数量

    • DelegatedScheduledExecutorService 类,是 Executors 类的内部类

2.1 定义方法

除了继承 ExecutorService 接口的方法外,ScheduledExecutorService 中还提供了另外的四个方法

  1. 创建一次定时任务,并指定触发延迟时间,任务传入 Runnable 参数
ScheduledFuture<?> schedule(Runnable command,long delay, TimeUnit unit);
  1. 创建一次定时任务,并指定触发延迟时间,任务传入 Callable 参数,并可以获取返回结果
<V> ScheduledFuture<V> schedule(Callable<V> callable,long delay, TimeUnit unit);
  1. 创建周期执行定时任务,可以参数设置第一次执行时的延迟时长,以及两次成功执行的时间间隔
ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnitunit);
  • 如果程序执行时间超过间隔时间,则会在上次执行结束后立即执行下次
  1. 创建周期执行任务,指定第一次执行延迟时长,以及上次结束后和下次开始间隔时长
ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnitunit);
  • 此种情况下,间隔时间是从上次任务执行完成后计算,因此可以保证任务的执行顺序

3. ScheduledExecutorService 使用

3.1 任务创建

ScheduledExecutorService提供的方法中需要的任务是 Runnable 或者 Callable<V> ,而实际上 java.util 中的 TimerTask 类也是实现了 Runnable 接口的。

我们可以直接定义任务类来实现 Runnable 接口,并将任务类作为 ScheduledExecutorService 的参数传递执行。

public class Task implements Runnable {
    @Override
    public void run() {
        System.out.println("任务执行,当前时间:"+ System.currentTimeMillis());
    }
}

3.2 使用示例

使用时先创建 ScheduledExecutorService 类型的对象,并执行对象的方法。

// 类中的 main 方法,执行定时任务
public static void main(String[] args){
    ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(1);
    scheduledExecutorService.scheduleAtFixedRate(new DoTaskBySchedule(),2,3, TimeUnit.SECONDS);
}

3.3 不足之处

ScheduledExecutorService 在作为定时任务执行时仍然会存在一定的不足,如:

  1. 没有可以根据时间指定的执行方法,需要传入延迟时间参数执行

  2. 指定定时任务时会以上次执行完成为前提,上次没有执行完则不会执行下次任务