1、线程池的核心参数
1、介绍
线程池的核心参数分为:核心线程数目、最大线程数目、非核心线程的空闲生存时间(非核心线程 = 最大线程数 - 核心线程数),时间单位、工作队列、线程工厂、当所有线程都在工作并且工作队列也放满时会触发拒绝策略。
ThreadPoolExecutor(
int corePoolSize, // 核心线程数(常驻线程)
int maximumPoolSize, // 最大线程数(核心+非核心)
long keepAliveTime, // 非核心线程空闲存活时间
TimeUnit unit, // 存活时间单位
BlockingQueue<Runnable> workQueue, // 任务队列
ThreadFactory threadFactory, // 线程工厂(定制线程)
RejectedExecutionHandler handler // 拒绝策略
)
2、核心线程数和最大线程数的区别
核心线程数是线程池长期保留的线程,即使空闲也不会销毁
最大线程数是线程池允许创建的最大线程数,最大线程数=核心线程 + 非核心线程
3、业务系统如何确定核心线程的配置数量
针对CPU密集型的处理任务(如计算、json转换),核心线程数配置 = CPU核心数 + 1
针对IO密集型的处理任务(如文件读写、DB读写、IO密集型),核心线程数配置 = 2 * CPU核心数 +1,如“电商下单场景中,IO密集型任务可设置
可以用如下代码查看CPU核心数
4、非核心线程空闲时如何销毁
- 当线程数 >
corePoolSize
时,空闲时间超过keepAliveTime
的非核心线程会被销毁。
5、常见任务队列
队列类型 | 特性 | 使用场景 |
---|---|---|
LinkedBlockingQueue | 无界队列(但是最大默认Integer.MAX_VALUE ),底层链表 | 允许短暂的高峰 |
ArrayBlockingQueue | 有界队列(需指定固定大小),底层基于数组结构 | 控制任务积压量 |
SynchronousQueue | 不存储任务,直接移交(插入需等待取出) | |
PriorityBlockingQueue | 优先级队列(任务需实现Comparable ) | 按优先级执行任务 |
实际开发中LinkedBlockingQueue用的较多,一般会设置一个默认大小。
6、拒绝策略怎么选择
策略名 | 行为 | 适用场景 |
---|---|---|
AbortPolicy (默认) | 直接抛出RejectedExecutionException | |
CallerRunsPolicy | 由提交任务的线程直接执行该任务 | 关键业务用 |
DiscardPolicy | 静默丢弃被拒绝的任务(不通知) | 非关键业务适用 |
DiscardOldestPolicy | 丢弃队列中最旧的任务,然后重试提交 |
2、线程池的执行流程
- 提交任务后,优先创建核心线程执行(直到数量达
核心线程数
)。 - 核心线程全忙时,新任务进入任务队列等待。
- 当队列满时,创建非核心线程执行任务(直到总数达
最大线程数
)。 - 若线程数已达最大值且队列已满,则触发拒绝策略。
3、线程池的种类(不建议使用)
1、固定线程数类型
2、单核心线程类型
3、可缓存线程类型
之所以叫可缓存线程池,是因为它在创建新线程时如果有可重用的线程,则重用它们,否则创建一个新线程并将其添加到线程池中;
4、周期延迟线程类型
4、为什么阿里巴巴禁止使用Executors
创建线程池?
适用Executors
创建线程池都是用的无界队列,当核心线程满时,如果一直往该线程池添加任务,会使任务队列变得非常大,最大可到Integer.MAX_VALUE。可能会造成OOM。
必须手动创建ThreadPoolExecutor
,明确指定队列大小和拒绝策略!
5、线程池常见的业务使用场景举例
1、数据批量导入
2、数据汇总查询
3、异步处理任务
用户在进行搜索时,我们希望保存搜索的关键字,并且不影响用户搜索的效率,这时就需要在执行搜索时,开启一个异步任务进行保存关键字,异步任务通过线程池来运行。如下代码案例
业务代码里调用保存方法,这个方法是在线程池里异步执行的,可以借助springboot里的注解@Async