(手机横屏看源码更方便)


注:java源码分析部分如无特殊说明均基于 java8 版本。

注:本文基于ScheduledThreadPoolExecutor定时线程池类。

简介

前面我们一起学习了普通任务、未来任务的执行流程,今天我们再来学习一种新的任务——定时任务。

定时任务是我们经常会用到的一种任务,它表示在未来某个时刻执行,或者未来按照某种规则重复执行的任务。

问题

(1)如何保证任务是在未来某个时刻才被执行?

(2)如何保证任务按照某种规则重复执行?

来个栗子

创建一个定时线程池,用它来跑四种不同的定时任务。

public class ThreadPoolTest03 {     public static void main(String[] args) throws ExecutionException, InterruptedException {         // 创建一个定时线程池         ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(5);          System.out.println("start: " + System.currentTimeMillis());          // 执行一个无返回值任务,5秒后执行,只执行一次         scheduledThreadPoolExecutor.schedule(() -> {             System.out.println("spring: " + System.currentTimeMillis());         }, 5, TimeUnit.SECONDS);          // 执行一个有返回值任务,5秒后执行,只执行一次         ScheduledFuture<String> future = scheduledThreadPoolExecutor.schedule(() -> {             System.out.println("inner summer: " + System.currentTimeMillis());             return "outer summer: ";         }, 5, TimeUnit.SECONDS);         // 获取返回值         System.out.println(future.get() + System.currentTimeMillis());          // 按固定频率执行一个任务,每2秒执行一次,1秒后执行         // 任务开始时的2秒后         scheduledThreadPoolExecutor.scheduleAtFixedRate(() -> {             System.out.println("autumn: " + System.currentTimeMillis());             LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));         }, 1, 2, TimeUnit.SECONDS);          // 按固定延时执行一个任务,每延时2秒执行一次,1秒执行         // 任务结束时的2秒后,本文由公从号“彤哥读源码”原创         scheduledThreadPoolExecutor.scheduleWithFixedDelay(() -> {             System.out.println("winter: " + System.currentTimeMillis());             LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));         }, 1, 2, TimeUnit.SECONDS);     } }

定时任务总体分为四种:

(1)未来执行一次的任务,无返回值;

(2)未来执行一次的任务,有返回值;

(3)未来按固定频率重复执行的任务;

(4)未来按固定延时重复执行的任务;

本文主要以第三种为例进行源码解析。

scheduleAtFixedRate()方法

提交一个按固定频率执行的任务。

public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,                                               long initialDelay,                                               long period,                                               TimeUnit unit) {     // 参数判断     if (command == null || unit == null)         throw new NullPointerException();     if (period <= 0)         throw new IllegalArgumentException();              // 将普通任务装饰成ScheduledFutureTask     ScheduledFutureTask<Void> sft =         new ScheduledFutureTask<Void>(command,                                       null,                                       triggerTime(initialDelay, unit),                                       unit.toNanos(period));     // 钩子方法,给子类用来替换装饰task,这里认为t==sft     RunnableScheduledFuture<Void> t = decorateTask(command, sft);     sft.outerTask = t;     // 延时执行     delayedExecute(t);     return t; }

可以看到,这里的处理跟未来任务类似,都是装饰成另一个任务,再拿去执行,不同的是这里交给了delayedExecute()方法去执行,这个方法是干嘛的呢?

delayedExecute()方法