Java 线程池入门详解:线程池基础定义 + ThreadPoolExecutor 全参构造方法逐参数解析

📚 目录

[1. 线程池是什么](#1. 线程池是什么)

[2. ThreadPoolExecutor 全参构造方法介绍](#2. ThreadPoolExecutor 全参构造方法介绍)

[3. 七大构造参数逐个详解](#3. 七大构造参数逐个详解)

[4. 完整代码演示](#4. 完整代码演示)

[5. 小结](#5. 小结)

前言:

  Java多线程开发里,手动new Thread创建线程弊端极多:频繁创建销毁线程开销大、线程无管控容易无限创建打满CPU、任务无法统一管理。线程池就是用来复用线程、管控任务的核心工具,也是面试高频考点。

1. 线程池是什么

 我们提前创建一批线程,放入到一个池子当中,当我们想要用其中一个线程时,我们可以直接从这个池子中取,用完这个线程时,又把线程放回到线程池中,不会销毁线程;

 这个就好比我们生活中的锅碗瓢盆,当我们想要使用其中一个东西的时候,我们可以直接在厨房中进行取,此时厨房就相当于一个线程池,我们做饭的东西都放在厨房中,锅碗瓢盆等待我们使用;

 总结:线程池是预先创建一批线程存放于池中,程序有异步任务时,直接复用池内空闲线程执行任务;任务执行完毕,线程不会销毁,回归池子等待复用。

 好处:

  1. 降低资源消耗->可以直接对比于一次性碗和陶瓷碗
  2. 提升相应速度->不用重复进行创建,好比做饭直接在厨房中进行拿去即可,不需要重新去超市买
  3. 方便管理 ->集中,并且日常厨房中不需要过多的锅碗瓢盆,我们可以主动限制买多少个
  4. 便于维护和运行->可以统计锅碗瓢盆的使用次数,和使用

[🔙 返回目录](#🔙 返回目录)


2. ThreadPoolExecutor 全参构造方法介绍

 Java中我们可以使用ThreadPoolExecutor构造器创建线程池,也可以使用Executors来创建线程池;

 具体使用哪一种方法,取决于公司的业务要求,ExecutorsJava官方对ThreadPoolExecutor进行进一步封装,方便我们日常中使用;

 而在阿里公司中明确规定书写线程池必须使用ThreadPoolExecutor,防止出现线程安全问题;

java 复制代码
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    //底层源码省略
}

[🔙 返回目录](#🔙 返回目录)


3. 七大构造参数逐个详解

corePoolSize 与 maximumPoolSize

corePoolSize :核心线程数,线程池中不会在空闲时自动回收,是线程池中时常保留的线程;

maximumPoolSize : 最大线程数,线程池中允许创建最大的线程数量,最大线程数 = 核心线程数+临时线程数

 举一个简单的例子:

 一家公司里面有5个正式员工,一天公司业务忙不过来了,老板有两个方案,一个是去招正式员工,一个去招实习生,如果

 招正式员工,等业务空闲的时候,员工就没事干,有的就开始摸鱼了,开除的话又需要考虑赔钱,此时老板就能去招实习

 业务忙的时候就多招实习生,业务不忙了就可以给实习生开了,不需要赔钱,此时一举两得;

 此时:正式员工就相当于核心线程,实习生就相当于临时的
[🔙 返回目录](#🔙 返回目录)


long keepAliveTime 与TimeUnit unit

 两个参数进行搭配使用,管控非核心临时线程的空闲回收规则;

keepAliveTime : 代表非核心线程等待任务的时间,当临时线程无事可做,超出等待实现时,JVA会销毁释放资源;

 默认只对非核心线程生效,如果调用调用threadPool.allowCoreThreadTimeOut(true) 开启后,核心线程同样会遵守该超时规则空闲回收。

unit : 时间单位枚举类TimeUnit,用来指定 keepAliveTime 的时间单位,常使用枚举:

  1. TimeUnit.SECONDS:秒(日常最常用)
  2. TimeUnit.MILLISECONDS:毫秒
  3. TimeUnit.MINUTES:分钟
  4. TimeUnit.HOURS:小时

 具体采用哪一个时间单位,根据需求进行选取;
[🔙 返回目录](#🔙 返回目录)


BlockingQueue<Runnable> workQueue

BlockingQueue<Runnable> workQueue : 阻塞队列,用于存放等待执行的runnable任务,当核心线程都处于忙碌状态时,新提交的任务不会创建新的线程,而是进入阻塞队列进行等待,当队列满之后,线程池才会创建非核心线程;

 主流使用的阻塞队列:

队列类型 特点 生产环境注意事项
ArrayBlockingQueue 有界队列,初始化必须指定固定容量 推荐日常业务使用,容量可控,不会无限堆积任务
LinkedBlockingQueue 可设置容量;不传参则为无界队列 无界模式任务可无限存入,极易引发 OOM 内存溢出,禁止裸用
SynchronousQueue 不存储任何任务,入队操作必须有线程同时接收 没有排队能力,任务进来直接触发扩容线程,线程容易拉满
  1. 队列是线程池流程的第二优先级:核心线程 > 队列 > 临时线程 > 拒绝策略;
  2. 高并发业务优先选用ArrayBlockingQueue限制队列长度,杜绝内存风险;
  3. Executors工具类很多默认使用无界LinkedBlockingQueue,这是阿里规范禁止直接使用 Executors 的核心原因。

[🔙 返回目录](#🔙 返回目录)


ThreadFactory threadFactory

 又名:线程工厂->专门用于自定义创建线程,线程池中所有新线程都由线程工厂产出;

 默认使用Executors.defaultThreadFactory(),创建普通非守护线程、无自定义线程名。

 线程工厂在生产环境中存在的价值:

  1. 自定义线程名称,线上排查日志、堆栈时,根据线程名快速区分业务线程,定位故障

  2. 可以设置守护线程,线程优先级

  3. 可以给线程绑定线程组,方便管理
    [🔙 返回目录](#🔙 返回目录)


RejectedExecutionHandler handler
 全称:拒绝策略,
 触发时机: 核心线程满、阻塞队列塞满、最大线程数也打满,线程池没有任何能力处理新任务时 ,执行拒绝策略。

拒绝策略类 行为逻辑 使用场景
AbortPolicy(默认) 直接抛出RejectedExecutionException异常,中断任务提交 普通业务,希望任务失败立刻报错,方便感知异常
CallerRunsPolicy 让提交任务的调用线程执行当前被拒绝任务 不允许丢任务,削峰限流,最慢但保证任务必执行
DiscardPolicy 默默丢弃新提交的任务,无报错无日志 允许丢任务,非核心日志、埋点等不重要任务
DiscardOldestPolicy 丢掉队列最老未执行任务,尝试放入新任务 时效性优先,新任务价值更高(消息实时推送)

 举一个简单的例子->门店客服:正式员工(核心线程)全忙、等候坐席(队列)坐满、临时兼职(最大线程)也全部在岗。

 再来新客户(新任务),拒绝策略决定怎么处理:

 Abort:直接拒绝接待,告知客户爆满报错;

 CallerRuns:前台接待员自己停下手里工作接待客户;

 Discard:假装没看见,忽略客户;

 DiscardOldest:把排队最久的客户赶走,接待新来的。

生产环境建议:

  1. 绝大多数业务推荐 CallerRunsPolicy,不会丢数据,天然限流;
  2. 定时、埋点、日志上报选 DiscardPolicy;
  3. 时效性强的推送业务用 DiscardOldestPolicy;
  4. 默认 AbortPolicy 容易线上大量抛异常,线上不推荐直接使用。

[🔙 返回目录](#🔙 返回目录)


4. 完整代码演示

java 复制代码
public class Demo1 {

    public static void main(String[] args) {
        //定义线程工厂
        ThreadFactory businessThreadFactory  = new ThreadFactory () {
            private final AtomicInteger threadNumber = new AtomicInteger(1);
            @Override
            public Thread newThread(Runnable r) {
                Thread thread = new Thread(r);
                thread.setName("thread-" + threadNumber.incrementAndGet());
                return thread;
            }
        };
        //定义拒绝策略
        RejectedExecutionHandler customHandler = new RejectedExecutionHandler() {

            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                System.out.println("任务被线程池拒绝,可自定义处理逻辑");
            }
        };
        //创建7个构造方法的线程池
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                3,                      // 核心线程数
                8,                                 // 最大线程数
                5L,                                // 非核心线程空闲存活时长
                TimeUnit.SECONDS,                  // 时间单位:秒
                new ArrayBlockingQueue<>(20),     // 有界阻塞队列,容量20
                businessThreadFactory,             // 自定义线程工厂
                customHandler                      // 自定义拒绝策略
        );

        // 批量提交30个测试任务
        for (int i = 1; i <= 30; i++) {
            int taskId = i;
            threadPool.submit(() -> {
                System.out.println(Thread.currentThread().getName() + " 执行任务:" + taskId);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

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


    }
}

 结果参考:


[🔙 返回目录](#🔙 返回目录)


5. 小结

 构造参数:

参数 作用
corePoolSize 常驻核心线程,不会被超时回收
maximumPoolSize 线程总数上限 = 核心线程 + 临时非核心线程
keepAliveTime 非核心线程空闲多久没人用就销毁
unit 空闲时间单位(时分秒毫秒)
workQueue 存放等待任务的阻塞队列,优先缓冲削峰
threadFactory 负责创建线程,推荐自定义命名方便排错
handler 线程池满载后的兜底拒绝方案

 执行流程:

提交任务 -> 核心线程数量没满:核心线程执行 -> 核心线程满:任务放入阻塞队列 -> 队列存满,创建线程数达到最大线程数 -> 最大线程满,执行拒绝策略

 线程池总结:

线程池通过复用线程减少频繁创建销毁线程的开销,流程是先走核心线程、再走队列、再扩容临时线程,最后触发拒绝;生产环境必须手动指定七大参数,约束队列长度、自定义线程名,根据业务选型拒绝策略,规避 OOM 和线上故障。
[🔙 返回目录](#🔙 返回目录)


相关推荐
m0_5873830013 分钟前
社区家政系统开发实战:从需求分析到上线部署全指南
java·spring boot·spring·需求分析
凤山老林18 分钟前
动态 i18n 体系落地:Spring Boot 多租户热加载与前后端协同实践
java·spring boot·后端·i18n
白露与泡影1 小时前
解密 Pi 的 Harness 工程:Agent 会话如何实现持久化与恢复
java·人工智能·算法
Littlehero_1211 小时前
QT自定义控件之热换站系统(源码开源)
开发语言·qt
meilindehuzi_a1 小时前
Python 基础语法(1):常量、变量、类型、输入输出与运算符
开发语言·python
caimouse2 小时前
ReactOS 窗口系统分析(5):可见区域与窗口 DC — vis.c + windc.c
c语言·开发语言
OPEN-F2 小时前
Python进阶教程:装饰器与生成器深入
开发语言·python
Zane19942 小时前
HashMap 为什么要在长度16、容量必须是2的幂这些细节上较劲
java·后端
长谷深风1112 小时前
为什么你的 Tool 总被模型选错?
java·大数据·ai·llm·ai agent·工具设计·agent设计
千里码aicood3 小时前
基于Python的电商数据采集系统(大数据+爬虫)
开发语言·python