前言

很多 Java 初学者、面试者、初级开发对线程池的认知只停留在 “用来管理线程、避免频繁创建销毁线程” 的浅层概念。

一旦遇到面试高频问题:七大核心参数、四种拒绝策略、五种线程池、工作原理、源码执行流程、为什么不推荐 Executors 创建线程池,就容易卡壳。

本篇文章从零开始,不讲废话、不跳知识点,从「进程与线程基础」→「多线程弊端」→「线程池诞生意义」→「核心参数详解」→「执行源码流程」→「各类线程池对比」→「实战使用规范」层层拆解,零基础也能彻底学懂线程池。

一、前置基础:彻底搞懂进程 & 线程

想要学会线程池,必须先搞懂线程是什么,这是所有知识点的根基。

1.1 什么是进程?

进程是操作系统资源分配的最小单位。

我们电脑上运行的每一个软件都是一个进程:微信、浏览器、IDEA、游戏。

进程拥有独立的内存空间、CPU 资源

进程之间相互隔离、互不干扰

进程创建、销毁开销极大

简单理解:进程就是一个独立运行的程序。

1.2 什么是线程?

线程是 CPU 调度执行的最小单位,也叫轻量级进程。

一个进程包含多个线程,线程共享当前进程的内存资源(堆内存、方法区),各自拥有独立的栈内存。

简单理解:

进程 = 工厂车间

线程 = 车间里的工人

车间资源(原料、机器)所有工人共享

工人干活需要 CPU 调度,工人可以并行工作

1.3 进程和线程的核心区别

资源隔离:进程完全隔离,线程共享进程资源

开销大小:进程创建销毁开销极大,线程开销极小

从属关系:一个进程至少有 1 个线程(主线程)

通信难度:进程间通信复杂,线程间共享变量即可通信

1.4 什么是多线程?

多线程:一个程序中开启多个线程,同时执行多个任务,目的是提高程序执行效率、充分利用 CPU 资源。

Java 程序默认自带主线程 main,我们手写的代码默认都在主线程执行。

二、为什么需要线程池?

学完线程基础,我们思考一个问题:我们直接 new Thread () 创建线程不行吗?为什么要用线程池?

2.1 手动创建线程的弊端

在业务开发中,如果频繁通过 new Thread() 创建线程处理任务,会出现四大致命问题:

1.创建销毁线程开销极高 线程虽然比进程轻量,但频繁创建、销毁线程,会消耗大量 CPU、内存资源,造成资源浪费。

2.无限制创建线程,极易导致 OOM 操作系统的线程数量是有上限的,如果大量任务到来,无限创建线程,会占用全部内存,导致内存溢出 OOM、程序卡死、宕机。

3.线程缺乏统一管理 手动创建的线程杂乱无章,没有统一的分配、调度、监控策略,任务混乱,无法控制并发数量。

4.任务响应速度慢 每次任务到来都需要新建线程,创建线程需要耗时,无法快速响应任务。

2.2 线程池的诞生意义

线程池就是一个线程缓存池,提前创建好若干线程,放入池中统一管理:

线程复用:线程执行完任务不销毁,放回池中,重复利用,避免频繁创建销毁

控制并发数:限制最大线程数,防止无限创建线程导致 OOM

提高响应速度:任务到来直接用空闲线程执行,无需新建

统一管理:统一调度、监控、执行任务,支持延迟、定时任务

一句话总结:线程池就是为了解决手动创建线程的所有弊端,是 Java 并发编程的核心工具。

三、线程池核心类体系

Java 线程池的核心类全部位于 java.util.concurrent 包下,核心体系如下:

3.1 核心继承关系

  1. Executor:顶层核心接口,只有一个方法 execute(),定义任务执行规范

  2. ExecutorService:继承 Executor,拓展了线程池关闭、任务提交、批量执行等方法

  3. ThreadPoolExecutor:真正的线程池实现类(我们工作中手动创建的线程池核心类)

  4. ScheduledThreadPoolExecutor:支持定时、延迟任务的线程池

  5. Executors:线程池工具类,快速创建各类线程池(阿里规范禁止使用)

重中之重:真正掌控线程池的是 ThreadPoolExecutor,所有线程池底层都是它!

四、线程池七大核心参数

ThreadPoolExecutor 构造方法包含七大核心参数,这是线程池所有原理的核心,下面逐个通俗透彻讲解。

完整构造方法:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)

4.1 核心参数逐个解析

1. corePoolSize 核心线程数

线程池中长期保留的线程数量,不会被销毁。

  • 线程池初始化时没有线程
  • 任务不断提交,线程数从 0 开始增加
  • 线程数小于核心线程数:优先新建核心线程处理任务
  • 核心线程一旦创建,常驻线程池,空闲也不会销毁

通俗理解:正式员工,长期在岗,不裁员。

2. maximumPoolSize 最大线程数

线程池允许创建的最大线程总数。 最大线程数 = 核心线程数 + 非核心线程数

  • 当核心线程全部繁忙、任务队列已满,才会创建非核心线程
  • 所有线程总数不能超过最大线程数

通俗理解:公司最大在岗人数(正式 + 临时工)。

3. keepAliveTime 空闲存活时间

非核心线程的空闲超时时间。

  • 非核心线程空闲时间超过该值,会自动销毁,释放资源

  • 核心线程不受该参数影响,永久存活

通俗理解:临时工闲置太久,自动辞退。

4. unit 时间单位

配合 keepAliveTime 的时间单位:毫秒、秒、分钟、小时等。

5. workQueue 任务阻塞队列

存储等待执行任务的队列,核心作用:缓冲任务。

当所有核心线程都在忙碌,新提交的任务不会新建线程,而是存入队列排队。

常用 4 种阻塞队列:

  1. ArrayBlockingQueue:有界队列,固定容量,超出容量触发非核心线程创建

  2. LinkedBlockingQueue:无界 / 有界队列,默认无界,可无限存任务(极易 OOM)

  3. SynchronousQueue:不存储任务的队列,来一个任务必须找线程执行

  4. DelayQueue:延迟队列,支持定时任务

6. threadFactory 线程工厂

用来创建线程的工厂,可以自定义:线程名称、线程优先级、是否守护线程。

作用:统一线程创建规则,方便日志排查、线程监控。

7. handler 拒绝策略

当线程池已满(线程数达最大值 + 队列已满),新任务到来的拒绝规则。

五、线程池完整工作原理

结合七大参数,我们梳理任务提交的完整执行流程,这是线程池的灵魂!

5.1 完整执行步骤

  1. 任务提交,判断当前核心线程数 < corePoolSize → 直接新建核心线程执行任务

  2. 核心线程已满(核心线程都在干活) → 新任务存入阻塞队列 workQueue排队

  3. 队列已满,无法存入任务 → 判断当前总线程数 < maximumPoolSize,新建非核心线程执行任务

  4. 总线程数达到最大值,队列也已满 → 触发拒绝策略,拒绝新任务

  5. 任务执行完毕 → 核心线程常驻,非核心线程等待 keepAliveTime 超时后销毁

5.2 通俗场景模拟

假设配置:核心线程 2,最大线程 5,队列容量 10

  1. 1-2 个任务:新建 2 个核心线程执行

  2. 3-12 个任务:核心线程忙碌,10 个任务进队列排队

  3. 13-15 个任务:队列满,新建 3 个非核心线程执行

  4. 16 个及以上任务:线程满、队列满,触发拒绝策略

六、四大拒绝策略

线程池满载后,新任务的四种处理策略,JDK 内置 4 种:

1. AbortPolicy(默认)

直接抛出异常 RejectedExecutionException,终止任务,线程池默认策略。 适合:需要及时发现任务失败的业务。

2. CallerRunsPolicy

谁提交的任务,谁自己执行(主线程执行)。 不会抛异常,会降低任务提交速度,削峰填谷。

3. DiscardPolicy

直接丢弃新任务,不抛异常、无日志。 适合:无关紧要、允许丢失的任务。

4. DiscardOldestPolicy

丢弃队列中最旧的未执行任务,执行当前新任务。 适合:时效性高、最新任务优先的业务。

七、JDK 五种内置线程池(原理 + 弊端)

JDK 通过 Executors 工具类提供 5 种常用线程池,但是阿里开发手册明确禁止使用,下面讲清原理和致命弊端。

1. newFixedThreadPool(固定线程池)

Executors.newFixedThreadPool(5);
  • 核心线程数 = 最大线程数,无非核心线程

  • 队列:无界 LinkedBlockingQueue

  • 弊端:队列无界,任务堆积无限扩容,导致 OOM

2. newSingleThreadExecutor(单线程池)

Executors.newSingleThreadExecutor();
  • 核心、最大线程数都是 1,单线程串行执行

  • 队列无界

  • 弊端:同样无限堆积任务,OOM 风险

3. newCachedThreadPool(缓存线程池)

Executors.newCachedThreadPool();
  • 核心线程 0,最大线程 Integer.MAX_VALUE

  • 队列:不存储任务的 SynchronousQueue

  • 弊端:可无限创建线程,瞬间耗尽 CPU 内存,直接 OOM 宕机

4. newScheduledThreadPool(定时线程池)

支持延迟、定时任务,最大线程数 MAX_VALUE,存在无限创建线程风险。

5. newWorkStealingPool(窃取线程池)

JDK1.8 新增,基于 ForkJoinPool,适合并行计算,不常用。

八、线程池核心源码执行流程(极简易懂)

我们看 execute() 方法核心源码,一眼看透底层:

public void execute(Runnable command) {
    // 1.任务非空校验
    if (command == null)
        throw new NullPointerException();
    
    // 获取线程池状态、线程数量
    int c = ctl.get();
    
    // 2.线程数小于核心线程数 → 新建核心线程
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    
    // 3.核心线程满,任务入队列
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        if (! isRunning(recheck) && remove(command))
            reject(command);
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
        return;
    }
    
    // 4.队列满,新建非核心线程,失败则拒绝
    if (!addWorker(command, false))
        reject(command);
}

九、生产环境线程池实战规范

9.1 手动创建线程池模板

// 自定义线程池(生产标准写法)
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
        5, // 核心线程数
        10, // 最大线程数
        30, // 空闲时间
        TimeUnit.SECONDS, // 时间单位
        new ArrayBlockingQueue<>(100), // 有界队列
        Executors.defaultThreadFactory(), // 线程工厂
        new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);

9.2 核心参数配置经验

  1. CPU 密集型任务(计算多、IO 少) 核心线程数 = CPU 核心数 + 1 充分利用 CPU,避免线程切换开销

  2. IO 密集型任务(数据库、网络请求多) 核心线程数 = CPU 核心数 * 2 IO 阻塞多,多开线程提升吞吐量

9.3 线程池使用最佳实践

  1. 禁止 Executors,全部手动创建 ThreadPoolExecutor

  2. 必须使用有界队列,限制任务数量

  3. 自定义线程工厂,设置线程名,方便排查问题

  4. 根据业务场景选择拒绝策略

  5. 项目关闭时手动关闭线程池,避免资源泄漏

结语

本篇文章从线程基础 → 线程池痛点 → 七大参数 → 执行原理 → 源码流程 → 实战规范,完整闭环讲解了 Java 线程池所有知识点。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐