一、为什么要使用ThreadPoolExecutor来创建线程池?
在阿里巴巴 Java 规范中,线程池是必须通过ThreadPoolExecutor来创建的,不能使用newFixedThreadPool 或 newCachedThreadPool 进行创建。
这是因为newFixedThreadPool 默认情况下会创建一个无界队列,可能会导致OOM问题。 newCachedThreadPool 默认的最大线程数是 Integer.MAX_VALUE 即会创建很多的线程最终导致 OOM 问题。
Executors 类提供的方法虽然简单,但他不仅隐藏了参数细节,还隐藏了你先发现问题的可能性。
因此,在创建线程池时,应该使用new ThreadPoolExecutor,按需指定对应的参数,避免因为错误的参数导致 OOM 问题。
二、ThreadPoolExecutor 主要参数
- corePoolSize:核心线程数
- maximumPoolSize:最大线程数
- keepAliveTime:空闲线程存活时间
- 拒绝策略
- 阻塞队列
- 线程工厂
@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 无实际存储空间的同步阻塞队列
拒绝策略
当线程数量大于最大线程数,线程会进入队列里等待。如果等待队列也满了,就需要根据拒绝策略来进行处理了。
默认情况下,会直接抛出异常。
此外,还有三个策略:
- DiscardPolicy:静默处理,装作没看见。
- DiscardOldestPolicy:将等待时间最长的任务抛弃掉
- 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
评论区