线程池ThreadPoolTaskExcutor详解

2,132 阅读5分钟

线程池ThreadPoolTaskExcutor详解

1、前言

ThreadPoolTaskExecutor线程是Spring提供的线程池,其底层是依据JDK线程池ThreadPoolExecutor来实现的。

2、核心参数配置

  • corePoolSize:线程池维护线程最小的数量,默认为1
  • maxPoolSize:线程池维护线程最大数量,默认为Integer.MAX_VALUE
  • keepAliveSeconds:(maxPoolSize-corePoolSize)部分线程空闲最大存活时间,默认存活时间是60s
  • queueCapacity:阻塞任务队列的大小,默认为Integer.MAX_VALUE,默认使用LinkedBlockingQueue
  • allowCoreThreadTimeOut:设置为true的话,keepAliveSeconds参数设置的有效时间对corePoolSize线程也有效,默认是flase
  • threadFactory::用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字。使用开源框架guava提供的ThreadFactoryBuilder可以快速给线程池里的线程设置有意义的名字
  • rejectedExecutionHandler:拒绝策略,当队列workQueue和线程池maxPoolSize都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。

3、ThreadPoolTaskExcutor内部执行流程

  • 通过 execute(Runnable)方法被添加到线程池,任务就是一个 Runnable类型的对象,任务当一个任务通过execute(Runnable)方法欲添加到线程池时:
  • 如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
  • 如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
  • 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
  • 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。
  • 也就是:处理任务的优先级为:
  • 核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。
  • 当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。
  • - unit可选的参数为java.util.concurrent.TimeUnit中的几个静态属性:NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS。

4、代码

package com.taikang.sp.base.service.springconfig;

import com.taikang.sp.base.service.impl.task.TaskExecutor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskRejectedException;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.concurrent.ListenableFuture;

import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;



@Configuration
@EnableAsync
@Slf4j
public class ExecutorConfiguration {

    /**
     * 线程池总数
     */
    @Value("${async.executor.thread.core_pool_size}")
    private int corePoolSize;
    /**
     * 最大线程池数
     */
    @Value("${async.executor.thread.max_pool_size}")
    private int maxPoolSize;
    /**
     * 队列大小
     */
    @Value("${async.executor.thread.queue_capacity}")
    private int queueCapacity;
    /**
     * 线程名前缀
     */
    @Value("${async.executor.thread.name.prefix}")
    private String namePrefix;

    @Bean(name = "asyncTaskExecutor")
    public Executor asyncServiceExecutor() {
        log.info("start asyncTaskExecutor");
        ThreadPoolTaskExecutor executor = new CustomThreadPollTaskExecutor();
        //配置核心线程数
        executor.setCorePoolSize(corePoolSize);
        //配置最大线程数
        executor.setMaxPoolSize(maxPoolSize);
        //配置队列大小
        executor.setQueueCapacity(queueCapacity);
        //配置线程池中的线程的名称前缀
        executor.setThreadNamePrefix(namePrefix);

        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(60);
        // rejection-policy:当pool已经达到max size的时候,如何处理新任务
        // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //执行初始化
        executor.initialize();
        return executor;
    }

    @Bean(initMethod = "executorInit")
    public TaskExecutor taskExecutor() {
        return new TaskExecutor();
    }


    @Slf4j
    public static final class CustomThreadPollTaskExecutor extends ThreadPoolTaskExecutor {

        private void showThreadPoolInfo(String prefix) {
            ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();

            if (null == threadPoolExecutor) {
                return;
            }

            log.info("{}, {},taskCount [{}], completedTaskCount [{}], activeCount [{}], queueSize [{}]",
                    this.getThreadNamePrefix(),
                    prefix,
                    threadPoolExecutor.getTaskCount(),
                    threadPoolExecutor.getCompletedTaskCount(),
                    threadPoolExecutor.getActiveCount(),
                    threadPoolExecutor.getQueue().size());
        }

        @Override
        public void execute(Runnable task) {
            showThreadPoolInfo("1. do execute");
            super.execute(task);
        }

        @Override
        public void execute(Runnable task, long startTimeout) {
            showThreadPoolInfo("2. do execute");
            super.execute(task, startTimeout);
        }

        @Override
        public Future<?> submit(Runnable task) {
            showThreadPoolInfo("1. do submit");
            return super.submit(task);
        }

        @Override
        public <T> Future<T> submit(Callable<T> task) {
            showThreadPoolInfo("2. do submit");
            return super.submit(task);
        }

        @Override
        public ListenableFuture<?> submitListenable(Runnable task) {
            showThreadPoolInfo("1. do submitListenable");
            return super.submitListenable(task);
        }

        @Override
        public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
            showThreadPoolInfo("2. do submitListenable");
            return super.submitListenable(task);
        }

    }

}

5、使用

使用1

@Resource(name = "asyncTaskExecutor")
private  ThreadPoolTaskExecutor asyncTaskExecutor;
@RequestMapping(value = "/executor",method = RequestMethod.POST,produces = "applications/json;charset=utf-8")
public void test() throws ExecutionException, InterruptedException {
    Random random = new Random();
    List<Future<Integer>> list=new ArrayList<>();
    int i=0;
    while (i<10) {
        list.add(asyncTaskExecutor.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int anInt = random.nextInt(100);
                System.out.println(Thread.currentThread().getName() + "====" + Thread.currentThread().getId() + anInt);
                return anInt;
            }
        }));
        i++;
    }
    for (Future future:list) {
        Object o = future.get();
        System.out.println(o+"=========="+"gzw");
    }
}

使用2

/**
 * 项目名称:spring-demo
 * 类 名 称:TestExecutor
 * 类 描 述:TODO
 * 创建时间:2022/3/10 11:27 上午
 *
 * @author gzw
 */
@Component
@DependsOn({"springContextUtils"})
public class TestExecutor {

    private final Map<Long,ExecutorMessage> messageMap;
    public TestExecutor(){
        ApplicationContext context = SpringContextUtils.getApplicationContext();
        Assert.notNull(context,"ApplicationContext is null");
        Map<String, ExecutorMessage> beans = context.getBeansOfType(ExecutorMessage.class);
        Assert.isTrue(!CollectionUtils.isEmpty(beans),"user is not null");
        messageMap=Collections.unmodifiableMap(beans.values().stream().collect(Collectors.toMap(ExecutorMessage::uniqueId,v->v)));
        Assert.isTrue(!CollectionUtils.isEmpty(messageMap),"以id 分组的spring bean map 为空");
    }

    @Resource(name = "asyncTaskExecutor")
    private  ThreadPoolTaskExecutor asyncTaskExecutor;
    @RequestMapping(value = "/executor",method = RequestMethod.POST,produces = "applications/json;charset=utf-8")
    public void test() throws ExecutionException, InterruptedException {
        Random random = new Random();
        List<Future<User>> list=new ArrayList<>();
            ExecutorMessage message = messageMap.get(0L).setUser(12);
            list.add(asyncTaskExecutor.submit(message));
        for (Future future:list) {
            Object o = future.get();
            System.out.println(o+"=========="+"gzw");
        }
    }

}

/**
 * 项目名称:spring-demo
 * 类 名 称:ExecutorMessage
 * 类 描 述:TODO
 * 创建时间:2022/3/10 7:54 下午
 *
 * @author gzw
 */
public abstract class ExecutorMessage implements Callable<User> {

    /**
     * 唯一标识
     * @return
     */
    public abstract Long uniqueId();


    /**
     * 真正执行的逻辑
     * @param id
     * @return
     */

    public abstract User handler(Integer id);


    private Integer id;


    public ExecutorMessage setUser(Integer id){
        this.id=id;
        return this;
    }


    @Override
    public User call() throws Exception {
        return handler(id);
    }
}

/**
 * 项目名称:spring-demo
 * 类 名 称:ExecutorHandler
 * 类 描 述:TODO
 * 创建时间:2022/3/10 8:04 下午
 *
 * @author gzw
 */
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)  //多实例模式
public class ExecutorHandler extends ExecutorMessage{
    @Override
    public Long uniqueId() {
        return 0L;
    }

    @Override
    public User handler(Integer id) {
        User user = new User();
        user.setName("gzw").setId(id);
        return user;
    }
}
/**
 * 项目名称:spring-demo
 * 类 名 称:ExecutorFuture
 * 类 描 述:TODO
 * 创建时间:2022/3/10 9:05 下午
 *
 * @author gzw
 */
@RestController
@RequestMapping("/test")
public class ExecutorFuture {
    @Autowired
    private TestExecutor testExecutor;

    @RequestMapping(value = "/executor",method = RequestMethod.POST,produces = "applications/json;charset=utf-8")
    public  void  learn() throws ExecutionException, InterruptedException {
        testExecutor.test();
        System.out.println("结束");
    }
}

6、总结

线程池提交任务有两种方法:

  1. 无返回值的任务使用public void execute(Runnable command) 方法提交;

    子线程可能会在主线程结束之后才结束

    而Runnable执行run时遇到异常并不会抛出

  2. 有返回值的任务使用public Future submit(Callable) 方法提交。

    因为提交任务后有个取数据的过程,在从Future取数据的过程中,Callable自带的阻塞机制,这个机制保证主线程一定在子线程结束之后结束。

    Callable执行call时遇到异常会抛出,