首页
学习
活动
专区
圈层
工具
发布

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 的使用。

创建一个待执行的任务:

代码语言:javascript
代码运行次数:0
复制

public class TaskDemo implements Runnable{

    private String message;

    public TaskDemo(String message) {
        this.message = message;
    }

    @Override
    public void run() {
        System.out.println(message);
    }
}

再创建一个执行任务的执行器:

代码语言:javascript
代码运行次数:0
复制

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;
    }
}

然后在容器中注入这两个类:

代码语言:javascript
代码运行次数:0
复制

<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>

再通过单元测试来执行代码:

代码语言:javascript
代码运行次数:0
复制

@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条消息。

3. TaskScheduler

TaskScheduler 用来执行定时任务,与 TaskExecutor 接口只提供了一个方法不同,TaskScheduler 接口提供了很多方法。

这些方法都接收一个 Runnable 实例,以及表示时间或者频率的参数。定时任务可以配置为执行一次,也可以配置为重复执行。

TaskSchduler 提供的方法如下:

代码语言:javascript
代码运行次数:0
复制

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 有三个实现:

  • ThreadPoolTaskScheduler:使用的比较多,是对 JDK中的 ScheduledThreadPoolExecutor 进行包装
  • ConcurrentTaskScheduler:同样也是对 ScheduledThreadPoolExecutor 进行包装,但是同时也继承了 ConcurrentTaskExecutor 来提供更好的并发度
  • DefaultManagedTaskScheduler:基于 JDNI 规范的实现,功能上与 ConcurrentTaskScheduler 相同

TaskScheduler 的使用

TaskScheduler 的使用和 TaskScheduler 类似。

代码语言:javascript
代码运行次数:0
复制

<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>
代码语言:javascript
代码运行次数:0
复制

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;
    }
}
代码语言:javascript
代码运行次数:0
复制

SpringSchedulerTaskDemo springTaskDemo = (SpringSchedulerTaskDemo) context.getBean("springSchedulerTaskDemo");
springTaskDemo.printMessage();

执行上面的代码之后,每隔5 秒钟就会打印一次消息。

4. task namespace

在 Spring 中,提供了 task 的 namespace,这样就可以少写很多代码。

在 xml 中假如如下 namespace:

代码语言:javascript
代码运行次数:0
复制
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 如下:

代码语言:javascript
代码运行次数:0
复制
<task:executor id="taskExecutor2" pool-size="5-10" queue-capacity="25" />

创建 TaskScheduler 如下:

代码语言:javascript
代码运行次数:0
复制
<task:scheduler id="threadPoolTaskScheduler" pool-size="1"/>

文 / Rayjun

  • 发表于:
  • 本文为 InfoQ 中文站特供稿件
  • 首发地址:https://www.infoq.cn/article/450f64ad3a617ee1bc0790b30
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。
领券