Java并发编程高级篇(七):使用执行器周期性地执行任务

我们已经学习了如何利用执行器框架提供的ThreadPoolExecutor类的线程池来执行任务,而不用我们手动去创建线程。同时我们也学习了,如何使用ScheduledThreadPoolExecutor类来延迟执行任务,如果你要指定任务执行的时间点,你只需要计算当前时间与目标时间的差值,把这个差值作为延迟时间即可实现定时执行任务。

接下来,如果我们想要周期性地执行一个任务,该怎么办呢。同样,我们可以使用ScheduledThreadPoolExecutor类来实现这个功能。

首先创建一个任务类,并实现Runnable接口。在call()方法中打印任务执行时间。

import java.util.Date;

/**
 * 新建Task类并实现Runnable接口
 *
 * 打印当前任务名+执行时间
 *
 * Created by hadoop on 2016/11/3.
 */
public class Task implements Runnable {
    private String name;

    public Task(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        System.out.printf("%s: Starting at : %s\n", name, new Date());
    }
}

然后我们创建主线程类。使用Executors工厂类的newScheduledThreadPoolExecutor来创建线程执行器,并调用它的scheduleAtFixedRate方法来周期性地执行任务,注意这个周期任务执行方法与schedule()延迟执行方法不同的是,它只能接受Runnable对象,并不支持Callable。

/**
     * 这个方法有四个参数
     * @command::需要执行的任务
     * @initialDelay:第一次执行延迟
     * @period:执行周期
     * @TimeUnit:时间单位
     */
public ScheduledFuture scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit)
/**
 * 在执行器中周期性的执行任务
 *
 * 上一节我们学习了使用ScheduledThreadPoolExecutor来延迟执行任务,这一节我们需要学如何周期性地执行任务。
 *
 * 要想周期性的执行任务,我们需要调用scheduleAtFixedRate(task, 1, 2, TimeUnit.SECONDS)方法。
 * 这个方法接受4个参数:
 *   第一个实现Runnable接口的任务实例
 *   第二个参数是第一次执行的延迟
 *   第三个参数是两次执行的时间间隔
 *   第四个参数是第二个参数和第三个参数的单位
 *
 * 这个方法返回一个ScheduledFuture类型的对象,我们可以通过这个对象的getDelay()方法来返回任务到下一次执行时所需要等待的时间。
 *
 * ScheduledThreadPoolExecutor还提供了另外一个方法scheduleWithFixedDelay()。
 * 这个方法与scheduleAtFixedRate()方法接收的参数相同。
 * 只不过它的第三个参数不是两次执行开始的间隔时间,而是上一次任务执行结束到下次任务之行开始的间隔时间。
 *
 * Created by hadoop on 2016/11/3.
 */
public class Main {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ScheduledThreadPoolExecutor executor = (ScheduledThreadPoolExecutor)Executors.newScheduledThreadPool(4);

        Task task = new Task("Task");

        ScheduledFuture future = executor.scheduleAtFixedRate(task, 1, 2, TimeUnit.SECONDS);
        //ScheduledFuture future = executor.scheduleWithFixedDelay(task, 1, 2, TimeUnit.SECONDS);

        for (int i =0; i < 10; i++) {
            System.out.printf("Main: Delay: %s. Result: %s\n", future.getDelay(TimeUnit.MILLISECONDS), future.get());

            TimeUnit.MILLISECONDS.sleep(1000);
        }

        executor.shutdown();

        executor.awaitTermination(1, TimeUnit.DAYS);
    }
}

我们每隔一秒钟打印一次,任务下次执行的时间间隔。其中有一点要说明的是,执行器给我们提供了两个周期任务执行方法。

  • scheduleAtFixedDelay():这个方法是从上一个任务执行开始就计算下次任务执行时间。
  • scheduleWithFixedDelay():这个方法是从上一个任务执行结束后才开始计算下次任务执行时间。

另外提一点,当设置执行器方法setContinueExistingPeriodicTasksAfterShutdownPolicy(true)为true的时候,即使关闭执行器,周期任务也会继续执行。

你可能感兴趣的:(Java并发编程高级篇(七):使用执行器周期性地执行任务)