侧边栏壁纸
  • 累计撰写 58 篇文章
  • 累计创建 67 个标签
  • 累计收到 1 条评论

多线程|线程池

lihaocheng
2021-06-03 / 0 评论 / 0 点赞 / 1,035 阅读 / 998 字
温馨提示:
晚上记得开启夜间模式哦

一、为什么要使用ThreadPoolExecutor来创建线程池?

在阿里巴巴 Java 规范中,线程池是必须通过ThreadPoolExecutor来创建的,不能使用newFixedThreadPool 或 newCachedThreadPool 进行创建。

这是因为newFixedThreadPool 默认情况下会创建一个无界队列,可能会导致OOM问题。 newCachedThreadPool 默认的最大线程数是 Integer.MAX_VALUE 即会创建很多的线程最终导致 OOM 问题。

Executors 类提供的方法虽然简单,但他不仅隐藏了参数细节,还隐藏了你先发现问题的可能性。

因此,在创建线程池时,应该使用new ThreadPoolExecutor,按需指定对应的参数,避免因为错误的参数导致 OOM 问题。

二、ThreadPoolExecutor 主要参数

  1. corePoolSize:核心线程数
  2. maximumPoolSize:最大线程数
  3. keepAliveTime:空闲线程存活时间
  4. 拒绝策略
  5. 阻塞队列
  6. 线程工厂
@EnableAsync(proxyTargetClass = true)
@Slf4j
@Configuration
public class MyThreadPool {

    @Bean
    public ThreadPoolExecutor threadPoolExecutor(){
        return new ThreadPoolExecutor(10, 20, 1L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<>(10),new CustomizableThreadFactory("myThreadPool"));
    }
}

阻塞队列

阻塞队列用于存放需要等待的

  • LinkedBlockingQueue 基于链表的阻塞队列
  • ArrayBlockingQueue 基于数组的有界队列
  • PriorityBlockingQueue 基于堆堆无解队列
  • SynchronousQueue 无实际存储空间的同步阻塞队列

拒绝策略

当线程数量大于最大线程数,线程会进入队列里等待。如果等待队列也满了,就需要根据拒绝策略来进行处理了。
默认情况下,会直接抛出异常。
此外,还有三个策略:

  1. DiscardPolicy:静默处理,装作没看见。
  2. DiscardOldestPolicy:将等待时间最长的任务抛弃掉
  3. CallerRunsPolicy:在任务提交者线程中执行任务

线程工厂

线程工厂可以指定线程的名字,方便问题的排查。

三、线程池工作策略

1.小于核心线程数

当有一个新任务到来时,如果目前的线程数小于 corePoolSize,就会直接创建新的线程进行处理,无论线程池中是否有空闲的线程。

2.大于等于核心线程数

当有一个新任务到来时,会先尝试进行排队,如果队列满了,就会检查线程个数是否达到了 maximumPoolSize,如果没到就创建新的线程

3.阻塞队列满,且大于最大线程数

按拒绝策略进行执行。

4.线程数大于核心线程数,有空闲的线程

等待 keepAliveTime 之后,如果依然没有任务需要处理,则收缩到核心线程数。

四、线程池要不要复用?

线程池的目的就是减少频繁创建和销毁线程的开销,因此复用线程池才是“不忘初心”。

但是,如果我们的业务中有需要大量计算,或者操作耗时比较久的任务,和其他任务共用一个线程池就会导致等待队列被占满。即使是一
些简单的任务,也不得不等待这些“慢”任务先处理完。

这种情况下,重新开一个线程池来处理这些“慢”任务,可以获得更好的性能。那么如何定义多个线程池呢?

如何定义多个线程池?

1. 配置类 MyThreadPoolConfig

这里需要指定 Bean name,否则会因为找到了多个 Bean 而报错。

@EnableAsync(proxyTargetClass = true)
@Configuration
public class MyThreadPoolConfig {
    private static final Integer CORE_SIZE=Runtime.getRuntime().availableProcessors();

    /**
     * 线程池1
     * @return
     */
    @Bean(name = "myThreadPoll1")
    public ThreadPoolExecutor myThreadPoll1(){
        return new ThreadPoolExecutor(CORE_SIZE, CORE_SIZE*2, 1000, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<>(100),new CustomizableThreadFactory("myThreadPoll1"));
    }

    /**
     * 线程池2
     * @return
     */
    @Bean(name = "myThreadPoll2")
    public ThreadPoolExecutor myThreadPoll2(){
        return new ThreadPoolExecutor(CORE_SIZE, CORE_SIZE*2, 1000, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<>(100),new CustomizableThreadFactory("myThreadPoll2"));
    }

}

2. Service 中使用线程池

@Autowired
private ThreadPoolExecutor myThreadPoll1
0

评论区