线程池的基本使用| 青训营笔记

60 阅读3分钟

这是我参与「第四届青训营 」笔记创作活动的第1天

线程池的好处

池化管理、减小开销。一般情况下的new Thread中,一个用户线程就创建一个内核线程,当请求数量多时,会出现频繁的创建和回收,不便于管理。

线程池的参数

线程池的真正实现类是 ThreadPoolExecutor,其构造方法有如下4种:

// 创建线程池
ThreadPoolExecutor threadPool 
                    = new ThreadPoolExecutor(CORE_POOL_SIZE,
                                             MAXIMUM_POOL_SIZE,
                                             KEEP_ALIVE,
                                             TimeUnit.SECONDS,
                                             sPoolWorkQueue,
                                             sThreadFactory);
// 线程池执行任务
threadPool.execute(new Runnable() {
    @Override
    public void run() {
    }
});

threadPool.shutdown(); // 关闭线程池

  • corePoolSize(核心线程数、初始化线程数)

    一般直接写cpu数量Runtime.getRuntime().availableProcessors(),线程池中的数量要多余这个数时,才会去利用空闲的线程。

  • maximumPoolSize(最大线程数)

    无空闲线程时,可创建的最大线程数量

    cpu密集型:核心数+1

    IO密集型:核心数*2

  • keepAliveTime(存活时间)

    回收的是非核心线程

  • workQueue(工作队列)

    不同任务有不同的策略

BlockingQueue特点
ArrayBlockingQueue一个由数组结构组成的有界阻塞队列。
LinkedBlockingQueue一个由链表结构组成的有界阻塞队列,在未指明容量时,容量默认为 Integer.MAX_VALUE。
PriorityBlockingQueue一个支持优先级排序的无界阻塞队列,对元素没有要求,可以实现 Comparable 接口也可以提供 Comparator 来对队列中的元素进行比较。跟时间没有任何关系,仅仅是按照优先级取任务。
DelayQueue类似于PriorityBlockingQueue,是二叉堆实现的无界优先级阻塞队列。要求元素都实现 Delayed 接口,通过执行时延从队列中提取任务,时间没到任务取不出来。
SynchronousQueue一个不存储元素的阻塞队列,消费者线程调用 take() 方法的时候就会发生阻塞,直到有一个生产者线程生产了一个元素,消费者线程就可以拿到这个元素并返回;生产者线程调用 put() 方法的时候也会发生阻塞,直到有一个消费者线程消费了一个元素,生产者才会返回。
LinkedBlockingDeque使用双向队列实现的有界双端阻塞队列。双端意味着可以像普通队列一样 FIFO(先进先出),也可以像栈一样 FILO(先进后出)。
LinkedTransferQueue它是ConcurrentLinkedQueue、LinkedBlockingQueue 和 SynchronousQueue 的结合体,但是把它用在 ThreadPoolExecutor 中,和 LinkedBlockingQueue 行为一致,但是是无界的阻塞队列
  • handler(拒绝策略)

当达到最大线程数时需要执行的饱和策略。就是管理的线程都繁忙下需要新线程,新来的任务的处理方案

handler特点
AbortPolicy(默认):丢弃任务并抛出 RejectedExecutionException 异常。
CallerRunsPolicy:由调用线程处理该任务。
DiscardPolicy:丢弃任务,但是不抛出异常。可以配合这种模式进行自定义的处理方式。
DiscardOldestPolicy:丢弃队列最早的未处理任务,然后重新尝试执行任务。

线程池拓展使用

springboot框架提供了@Async注解,帮助我们更方便的将业务逻辑提交到线程池中异步执行

spring中的ThreadPoolTaskExecutor对ThreadPoolExecutor封装,支持线程池的bean化,防止滥用线程池。

声明线程池

@Configuration
@EnableAsync
public class ExecutorConfig {

    private static final Logger logger = LoggerFactory.getLogger(ExecutorConfig.class);

    @Bean
    public Executor asyncServiceExecutor() {
        logger.info("start asyncServiceExecutor");
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //配置核心线程数
        executor.setCorePoolSize(5);
        //配置最大线程数
        executor.setMaxPoolSize(5);
        //配置队列大小
        executor.setQueueCapacity(99999);
        //配置线程池中的线程的名称前缀
        executor.setThreadNamePrefix("async-service-");

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

service层声明业务

    @Override
    @Async("asyncServiceExecutor")
    public void executeAsync() {
        logger.info("start executeAsync");
        try{
            Thread.sleep(1000);
        }catch(Exception e){
            e.printStackTrace();
        }
        logger.info("end executeAsync");
    }