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 和线上故障。
[🔙 返回目录](#🔙 返回目录)


相关推荐
一条泥憨鱼1 小时前
【从0开始学习计算机网络】| HTTP方法疑点解析
开发语言·计算机网络·http
老白干4 小时前
jjwt 0.9.1 在 JDK 11+ 上的两个“坑”与完整解决方案
java·python·log4j
圆山猫10 小时前
[Virtualization](四):Linux KVM/RISC-V 的 vCPU 运行路径
java·linux·risc-v
微学AI10 小时前
一根针指向所有方向:挂谷猜想对 LLM Agent 技能-记忆架构的启示
开发语言·人工智能·架构·挂谷猜想
城管不管11 小时前
ReAct、Plan-and-Execute、Reflection 三大智能 Agent 范式核心区别
java·人工智能·算法·spring·ai·动态规划
IT小白杨11 小时前
从环境制备到自动化工作流:多账号运营的工程化架构拆解
java·经验分享·自动化·安全架构·指纹浏览器
豆瓣鸡11 小时前
算法日记 - Day3
java·开发语言·算法
萧瑟余晖11 小时前
Java深入解析篇九之NIO详解
java·网络·nio
韭菜炒鸡肝天12 小时前
VTK开发笔记(一):VTK介绍,Qt..+VSx+VTK.编译
开发语言·笔记·qt