ThreadPoolTaskExecutor

1,687 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第11天,点击查看活动详情

一、简介

ThreadPoolTaskExecutor是spring框架基于JDK中ThreadPoolExecutor的,在它的基础上进行了一层封装。Spring通过配置的方式把ThreadPoolExecutor的核心属性暴露出来,让用户可以自定义配置并注入到当前容器,由容器进行管理。

二、使用

目前应用大多是基于springBoot开发,在springBoot中有默认的对线程池的配置

主要通过TaskExecutionAutoConfiguration这个类进行配置,核心属性在TaskExecutionProperties中,可通过JAVAConfig的方式,修改线程池的配置。通过配置可以把ThreadPoolTaskExecutor注入到容器,并且覆盖springBoot原本的配置信息

  @Bean
  public ThreadPoolTaskExecutor taskExecutor() {
    ThreadPoolTaskExecutor poolExecutor = new ThreadPoolTaskExecutor();
    // 核心线程数
    poolExecutor.setCorePoolSize(5);
    // 最大线程数
    poolExecutor.setMaxPoolSize(15);
    // 队列大小
    poolExecutor.setQueueCapacity(100);
    // 线程最大空闲时间
    poolExecutor.setKeepAliveSeconds(300);
    // 拒绝策略
    poolExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    // 线程名称前缀
    poolExecutor.setThreadNamePrefix("my-task-pool-");

    return poolExecutor;
  }

通过配置文件方式修改配置信息

# 核心线程数
spring.task.execution.pool.core-size=8  
# 最大线程数
spring.task.execution.pool.max-size=16
# 空闲线程存活时间
spring.task.execution.pool.keep-alive=60s
# 是否允许核心线程超时
spring.task.execution.pool.allow-core-thread-timeout=true
# 线程队列数量
spring.task.execution.pool.queue-capacity=100
# 线程关闭等待
spring.task.execution.shutdown.await-termination=false
spring.task.execution.shutdown.await-termination-period=
# 线程名称前缀
spring.task.execution.thread-name-prefix=task-

ThreadPoolExecutor主要有以下几个方法

public void execute(Runnable task)
public void execute(Runnable task, long startTimeout)
public Future<?> submit(Runnable task)
<T> Future<T> submit(Callable<T> task)
public ListenableFuture<?> submitListenable(Runnable task) 
public <T> ListenableFuture<T> submitListenable(Callable<T> task)
  1. 相比 ThreadPoolExecutor,ThreadPoolTaskExecutor 增加了 submitListenable 方法,该方法返回 ListenableFuture 接口对象
  2. ListenableFuture 接口对象,增加了线程执行完毕后成功和失败的回调方法。从而避免了 Future 需要以阻塞的方式调用 get,然后再执行成功和失败的方法。
  3. ListenableFuture 相比 Future 是不需要知道 执行结果的情况下就可以将 成功或者失败的业务代码 通过回调的方式 预埋,带来的好处就是异步,不需要阻塞当前线程,从而可以提高系统的吞吐量。
  4. Future 需要通过 get() 方法阻塞当前线程,在获取线程的执行结果后再根据执行结果编写相关的业务代码

三、例子

public static void main(String[] args) {
    ThreadPoolTaskExecutor executorService = new ThreadPoolTaskExecutor();
    executorService.setCorePoolSize(1);
    executorService.setMaxPoolSize(1);
    executorService.initialize();

    long start = System.currentTimeMillis();
    for (int i = 0; i < 10000; i++) {
        ListenableFuture<Boolean> asyncResult = executorService.submitListenable(() -> {

            // 休息5毫秒,模拟执行
            TimeUnit.MILLISECONDS.sleep(5);
            //throw new RuntimeException("出现异常");
            return true;

        });
        asyncResult.addCallback(data -> {
                    try {
                        // 休息3毫秒模拟获取到执行结果后的操作
                        TimeUnit.MILLISECONDS.sleep(3);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }, ex -> logger.info("**异常信息**:{}", ExceptionUtils.getExceptionMsg(ex)));
    }
    System.out.println(String.format("总结耗时:%s", System.currentTimeMillis() - start));
}

四、总结

spring中@Async也是基于线程池,如果有自定义的线程池就此注解的方法就使用自定义的线程池。使用@Async需要在启动上增加注解,并且在需要异步执行的方法上增加@Async注解。

@SpringBootApplication
@EnableAsync
public class ThreadpoolApplication {
    public static void main(String[] args) {
        SpringApplication.run(ThreadpoolApplication.class, args);
    }
}