我们已经学习了如何利用执行器框架提供的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<String> 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的时候,即使关闭执行器,周期任务也会继续执行。