转载请注明 http://tramp.cincout.cn/2016/09/30/spring-task-and-schedule-deep-research/
摘要
在很多业务场景中,系统都需要用到任务调度系统。例如定期地清理Redis 缓存,周期性地检索某一条件并更新系统的资源等。在现代的应用系统中,快速地响应用户的请求,是用户体验最主要的因素之一。因此在Web 系统中异步地执行任务,也会在很多场景中经常涉及到。本文对任务调度和异步执行的Java 实现进行了总结,主要讲述一下内容:
- Java 对异步执行和任务调度的支持
- Spring 4.X 的异步执行和任务调度实现
Java 对异步执行和任务调度的支持
异步执行和任务调度底层的语言支撑都是Java 的多线程技术。线程是系统进行独立运行和调度的基本单位。拥有了多线程,系统就拥有了同时处理多项任务的能力。
Java 实现异步调用
在Java 中要实现多线程有实现Runnable 接口和扩展Thread 类两种方式。只要将需要异步执行的任务放在run() 方法中,在主线程中启动要执行任务的子线程就可以实现任务的异步执行。如果需要实现基于时间点触发的任务调度,就需要在子线程中循环的检查系统当前的时间跟触发条件是否一致,然后触发任务的执行。该内容属于Java 多线程的基础知识,此处略过不讲。
Java Timer 和 TimeTask 实现任务调度
为了便于开发者快速地实现任务调度,Java JDK 对任务调度的功能进行了封装,实现了Timer 和TimerTask 两个工具类。
由上图,我们可以看出TimeTask 抽象类在实现Runnable 接口的基础上增加了任务cancel() 和任务scheduledExecuttionTime() 两个方法。
上图为调度类Timer 的实现。从Timer类的源码,可以看到其采用TaskQueue 来实现对多个TimeTask 的管理。TimerThread 集成自Thread 类,其mainLoop() 用来对任务进行调度。而Timer 类提供了四种重载的schedule() 方法和重载了两种sheduleAtFixedRate() 方法来实现几种基本的任务调度类型。下面的代码是采用Timer 实现的定时系统时间打印程序。
public class PrintTimeTask extends TimerTask {
@Override
public void run() {
System.out.println(new Date().toString());
}
public static void main(String[] args) {
Timer timer = new Timer("hello");
timer.schedule(new PrintTimeTask(), 1000L, 2000L);
}
}
Spring 4.x 中的异步执行和任务调度
Spring 4.x 中的异步执行
Spring 作为一站式框架,为开发者提供了异步执行和任务调度的抽象接口TaskExecutor 和TaskScheduler。Spring 对这些接口的实现类支持线程池(Thread Pool) 和代理。
Spring 提供了对JDK 中Timer和开源的流行任务调度框架Quartz的支持。Spring 通过将关联的Schedule 转化为FactoryBean 来实现。通过Spring 调度框架,开发者可以快速地通过MethodInvokingFactoryBean 来实现将POJO 类的方法转化为任务。
Spring TaskExecutor
TaskExecutor 接口扩展自java.util.concurrent.Executor 接口。TaskExecutor 被创建来为其他组件提供线程池调用的抽象。
ThreadPoolTaskExecutor 是TaskExecutor 的最主要实现类之一。该类的核心继承关系如下图所示。