Spring 中的线程池与任务调度
线程池已经成为 Java 开发中必不可少的一个组件了,在使用 Spring 时,不需要自己重头去使用线程池。
Spring 已经提供了非常完备的封装,可以直接使用 Spring 提供的接口。
💡本文基于 Spring5.3 和 OpenJDK11
1. Spring 中的任务
Spring 中对与任务的执行提供了两种抽象, TaskExecutor
和 TaskScheduler
,分别表示执行异步任务和定时任务。
Executor
在 JDK 中是线程池的名称。一个 executor 用来表示执行任务的线程池,其中最少会有一个线程,每个线程都可以用来执行同步或者异步任务。
Scheduler
表示的是定时任务,定时任务的触发,支持 JDK 中的 Timer
和 Quartz Scheduler
。
2. TaskExecutor
TaskExecutor 接口继承了 JDK 中的 Executor。在 JDK 中,ThreadPoolExecutor 继承了 Executor,也是一个很常用的接口。
Spring 对这些实现屏蔽了细节,无论是开发 Java EE 应用还是 Java SE 应用,都可以直接使用 TaskExecutor。
TaskExecutor 的实现
Spring 中已经实现了多种类型的 TaskExecutor,在绝大多数情况下,不需要自己去实现。
SyncTaskExecutor:用来执行非异步的任务,通常用于不需要多线程的场景,实际用的比较少,通常用来执行测试用例
SimpleAsyncTaskExecutor:这个实现不会重用任何的线程,每当有新任务的时候,都是重新创建一个线程
ConcurrentTaskExecutor:这个实现是对 Executor 的适配,可以配置 Executor 的全部参数,但是一般很少使用,除非需要完全自主配置线程池
ThreadPoolTaskExecutor:这个实现最常用,其中封装了 ThreadPoolExecutor,如果还需要使用 Executor 的其他实现,可以使用 ConcurrentTaskExecutor
WorkManagerTaskExecutor:这个用的就更少了,这个实现封装了 WebLogic 的 API,以便在 WebLogic 中间件上运行 Spring 程序
DefaultManagedTaskExecutor:这个实现的目标是替代 WorkManagerTaskExecutor。
TaskExecutor 的使用
下面以最常见的 ThreadPoolTaskExecutor 为例来演示 TaskExecutor 的使用。
创建一个待执行的任务:
再创建一个执行任务的执行器:
然后在容器中注入这两个类:
再通过单元测试来执行代码:
控制台中会输出10条消息。
3. TaskScheduler
TaskScheduler 用来执行定时任务,与 TaskExecutor 接口只提供了一个方法不同,TaskScheduler 接口提供了很多方法。
这些方法都接收一个 Runnable 实例,以及表示时间或者频率的参数。定时任务可以配置为执行一次,也可以配置为重复执行。
TaskSchduler 提供的方法如下:
TaskScheduler 实现
TaskScheduler 有三个实现:
ThreadPoolTaskScheduler:使用的比较多,是对 JDK中的 ScheduledThreadPoolExecutor 进行包装
ConcurrentTaskScheduler:同样也是对 ScheduledThreadPoolExecutor 进行包装,但是同时也继承了 ConcurrentTaskExecutor 来提供更好的并发度
DefaultManagedTaskScheduler:基于 JDNI 规范的实现,功能上与 ConcurrentTaskScheduler 相同
TaskScheduler 的使用
TaskScheduler 的使用和 TaskScheduler 类似。
执行上面的代码之后,每隔5 秒钟就会打印一次消息。
4. task namespace
在 Spring 中,提供了 task 的 namespace,这样就可以少写很多代码。
在 xml 中假如如下 namespace:
然后上面创建 TaskExecutor 如下:
创建 TaskScheduler 如下:
文 / Rayjun
本文首发于微信公众号
版权声明: 本文为 InfoQ 作者【Rayjun】的原创文章。
原文链接:【http://xie.infoq.cn/article/450f64ad3a617ee1bc0790b30】。文章转载请联系作者。
评论 (4 条评论)