线程池已经成为 Java 开发中必不可少的一个组件了,在使用 Spring 时,不需要自己重头去使用线程池。
Spring 已经提供了非常完备的封装,可以直接使用 Spring 提供的接口。
💡本文基于 Spring5.3 和 OpenJDK11
Spring 中对与任务的执行提供了两种抽象, TaskExecutor
和 TaskScheduler
,分别表示执行异步任务和定时任务。
Executor
在 JDK 中是线程池的名称。一个 executor 用来表示执行任务的线程池,其中最少会有一个线程,每个线程都可以用来执行同步或者异步任务。
Scheduler
表示的是定时任务,定时任务的触发,支持 JDK 中的 Timer
和 Quartz Scheduler
。
TaskExecutor 接口继承了 JDK 中的 Executor。在 JDK 中,ThreadPoolExecutor 继承了 Executor,也是一个很常用的接口。
Spring 对这些实现屏蔽了细节,无论是开发 Java EE 应用还是 Java SE 应用,都可以直接使用 TaskExecutor。
Spring 中已经实现了多种类型的 TaskExecutor,在绝大多数情况下,不需要自己去实现。
下面以最常见的 ThreadPoolTaskExecutor 为例来演示 TaskExecutor 的使用。
创建一个待执行的任务:
public class TaskDemo implements Runnable{ private String message; public TaskDemo(String message) { this.message = message; } @Override public void run() { System.out.println(message); } }
再创建一个执行任务的执行器:
public class SpringTaskDemo { private ThreadPoolTaskExecutor threadPoolTaskExecutor; public void printMessage() { for(int i = 0; i < 10; i++) { threadPoolTaskExecutor.execute(new TaskDemo("Hello rayjun " + i)); } } public void setThreadPoolTaskExecutor(ThreadPoolTaskExecutor threadPoolTaskExecutor) { this.threadPoolTaskExecutor = threadPoolTaskExecutor; } }
然后在容器中注入这两个类:
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"> <property name="corePoolSize" value="5"/> <property name="maxPoolSize" value="10"/> <property name="queueCapacity" value="25"/> </bean> <bean id="springTaskDemo" class="cn.rayjun.spring.SpringTaskDemo"> <property name="threadPoolTaskExecutor" ref="taskExecutor"/> </bean>
再通过单元测试来执行代码:
@ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:applicationContext.xml") class SpringTaskDemoTest { @Autowired private ApplicationContext context; @Test public void test1() { SpringTaskDemo springTaskDemo = (SpringTaskDemo) context.getBean("springTaskDemo"); springTaskDemo.printMessage(); } }
控制台中会输出10条消息。
TaskScheduler 用来执行定时任务,与 TaskExecutor 接口只提供了一个方法不同,TaskScheduler 接口提供了很多方法。
这些方法都接收一个 Runnable 实例,以及表示时间或者频率的参数。定时任务可以配置为执行一次,也可以配置为重复执行。
TaskSchduler 提供的方法如下:
public interface TaskScheduler { ScheduledFuture schedule(Runnable task, Trigger trigger); ScheduledFuture schedule(Runnable task, Instant startTime); ScheduledFuture schedule(Runnable task, Date startTime); ScheduledFuture scheduleAtFixedRate(Runnable task, Instant startTime, Duration period); ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period); ScheduledFuture scheduleAtFixedRate(Runnable task, Duration period); ScheduledFuture scheduleAtFixedRate(Runnable task, long period); ScheduledFuture scheduleWithFixedDelay(Runnable task, Instant startTime, Duration delay); ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay); ScheduledFuture scheduleWithFixedDelay(Runnable task, Duration delay); ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay); }
TaskScheduler 有三个实现:
TaskScheduler 的使用和 TaskScheduler 类似。
<bean id="taskSchedulerExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler"> <property name="poolSize" value="1"/> </bean> <bean id="springSchedulerTaskDemo" class="cn.rayjun.spring.SpringSchedulerTaskDemo"> <property name="threadPoolTaskScheduler" ref="taskSchedulerExecutor"/> </bean>
public class SpringSchedulerTaskDemo { private ThreadPoolTaskScheduler threadPoolTaskScheduler; public void printMessage() { threadPoolTaskScheduler.schedule(new TaskDemo("Ray"), new CronTrigger("0/5 * * * * ?")); } public void setThreadPoolTaskScheduler(ThreadPoolTaskScheduler threadPoolTaskScheduler) { this.threadPoolTaskScheduler = threadPoolTaskScheduler; } }
SpringSchedulerTaskDemo springTaskDemo = (SpringSchedulerTaskDemo) context.getBean("springSchedulerTaskDemo"); springTaskDemo.printMessage();
执行上面的代码之后,每隔5 秒钟就会打印一次消息。
在 Spring 中,提供了 task 的 namespace,这样就可以少写很多代码。
在 xml 中假如如下 namespace:
xmlns:task="http://www.springframework.org/schema/task"xsi:schemaLocation="http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd"
然后上面创建 TaskExecutor 如下:
<task:executor id="taskExecutor2" pool-size="5-10" queue-capacity="25" />
创建 TaskScheduler 如下:
<task:scheduler id="threadPoolTaskScheduler" pool-size="1"/>
文 / Rayjun