线程池已经成为 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
领取专属 10元无门槛券
私享最新 技术干货