什么是线程池?线程池的作用?线程池的四种创建方法?

一、什么是线程池?

线程池是预先创建一组可复用的线程,由线程池管理器统一管理线程的生命周期(创建、分配任务、执行、回收),避免频繁创建 / 销毁线程的开销,实现任务与线程的解耦。

简单理解:线程池就像一个 "工人团队",线程是 "工人",任务是 "工作"。提前招聘好工人(创建线程),有工作时分配给工人,工作完成后工人回到团队待命,而非直接解雇(避免重复招聘 / 解雇的成本)。

二、线程池的核心作用

  1. 降低资源消耗:线程创建 / 销毁需要操作系统分配内核资源(如 PCB、栈空间),频繁操作会消耗大量 CPU / 内存,线程池复用线程,减少此类开销。
  2. 提高响应速度:任务到达时,无需等待线程创建,直接分配给空闲线程执行,缩短任务等待时间。
  3. 控制并发数量:避免无限制创建线程导致的系统资源耗尽(如 OOM),通过核心线程数、最大线程数限制并发度。
  4. 统一管理线程:支持任务队列、拒绝策略、线程空闲超时回收等,便于监控和调优(如统计任务执行效率、线程利用率)。
  5. 支持任务缓存与调度:通过任务队列缓存待执行任务,支持 FIFO、优先级等调度策略。

三、线程池的核心参数(Java 为例,奠定理解基础)

后续创建方法依赖核心参数,先明确ThreadPoolExecutor的 7 个核心参数(所有 Java 线程池底层均基于此类):

参数名 作用
corePoolSize 核心线程数:线程池长期维持的最小线程数(即使空闲也不会回收,除非设置allowCoreThreadTimeOut
maximumPoolSize 最大线程数:线程池允许创建的最大线程数(核心线程忙 + 队列满时,才会创建临时线程)
keepAliveTime 临时线程空闲超时时间:临时线程(超过核心线程数的线程)空闲超过该时间则被回收
unit keepAliveTime的时间单位(如TimeUnit.SECONDS
workQueue 任务队列:缓存待执行任务的队列(如LinkedBlockingQueueSynchronousQueue
threadFactory 线程工厂:用于创建线程(可自定义线程名称、优先级、是否守护线程等)
handler 拒绝策略:任务队列满 + 线程数达最大时,如何处理新提交的任务(如丢弃、抛出异常、由提交线程执行)

四、线程池的四种创建方法(Java 实战,最常用)

Java 通过Executors工具类提供 4 种便捷创建方法(底层均封装ThreadPoolExecutor),同时补充自定义线程池(推荐生产环境使用,避免默认方法的隐患)。

前提:统一任务类(所有示例复用)

定义一个 Runnable 任务,用于模拟业务逻辑:

java 复制代码
// 模拟业务任务:打印线程名+任务编号,耗时1秒
class MyTask implements Runnable {
    private final int taskId;

    public MyTask(int taskId) {
        this.taskId = taskId;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " 执行任务 " + taskId);
        try {
            Thread.sleep(1000); // 模拟任务耗时
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " 完成任务 " + taskId);
    }
}

方法 1:FixedThreadPool(固定线程数线程池)

特点
  • 核心线程数 = 最大线程数(无临时线程),线程空闲时不会回收。
  • 任务队列是无界队列(LinkedBlockingQueue),可缓存大量任务。
  • 适用于:任务量稳定、需要控制并发度的场景(如服务器接收请求)。
代码实现
java 复制代码
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class FixedThreadPoolDemo {
    public static void main(String[] args) {
        // 1. 创建固定线程数线程池(核心线程数=3,最大线程数=3)
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);

        // 2. 提交5个任务(线程池只有3个线程,会复用)
        for (int i = 1; i <= 5; i++) {
            fixedThreadPool.submit(new MyTask(i)); // 提交任务
        }

        // 3. 关闭线程池(不再接收新任务,等待已提交任务执行完毕)
        fixedThreadPool.shutdown();
    }
}
执行结果(关键观察)
  • 只有 3 个线程(pool-1-thread-1/2/3)循环执行 5 个任务,无新线程创建。

  • 任务 4、5 会在队列中等待,直到有线程空闲。

    pool-1-thread-1 执行任务 1
    pool-1-thread-2 执行任务 2
    pool-1-thread-3 执行任务 3
    pool-1-thread-1 完成任务 1
    pool-1-thread-1 执行任务 4
    pool-1-thread-2 完成任务 2
    pool-1-thread-2 执行任务 5
    pool-1-thread-3 完成任务 3
    pool-1-thread-1 完成任务 4
    pool-1-thread-2 完成任务 5

底层原理(简化)
注意隐患

无界队列可能导致任务堆积过多,最终触发 OOM(内存溢出),生产环境需谨慎使用(或自定义队列大小)。

方法 2:CachedThreadPool(缓存线程池)

特点
  • 核心线程数 = 0,最大线程数 =Integer.MAX_VALUE(理论上无上限)。
  • 任务队列是同步队列(SynchronousQueue):队列无容量,提交任务时必须有线程接收,否则立即创建新线程。
  • 临时线程空闲超时时间 = 60 秒:空闲超过 60 秒的线程会被回收。
  • 适用于:任务量大但执行时间短、并发度波动大的场景(如临时批量处理小任务)。
代码实现
java 复制代码
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CachedThreadPoolDemo {
    public static void main(String[] args) {
        // 1. 创建缓存线程池(核心线程数=0,最大线程数无上限)
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

        // 2. 提交5个任务(任务执行耗时1秒,会创建5个临时线程)
        for (int i = 1; i <= 5; i++) {
            cachedThreadPool.submit(new MyTask(i));
        }

        // 3. 关闭线程池
        cachedThreadPool.shutdown();
    }
}
执行结果(关键观察)
  • 5 个任务同时提交,创建 5 个临时线程(pool-1-thread-1~5),并行执行。

  • 任务执行完毕后,线程空闲 60 秒会被回收。

    pool-1-thread-1 执行任务 1
    pool-1-thread-2 执行任务 2
    pool-1-thread-3 执行任务 3
    pool-1-thread-4 执行任务 4
    pool-1-thread-5 执行任务 5
    pool-1-thread-1 完成任务 1
    pool-1-thread-2 完成任务 2
    pool-1-thread-3 完成任务 3
    pool-1-thread-4 完成任务 4
    pool-1-thread-5 完成任务 5

底层原理(简化)
java 复制代码
// Executors.newCachedThreadPool() 底层代码
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(
        0,                  // 核心线程数=0
        Integer.MAX_VALUE,  // 最大线程数无上限
        60L,                // 临时线程空闲超时60秒
        TimeUnit.SECONDS,
        new SynchronousQueue<Runnable>() // 同步队列(无容量)
    );
}
注意隐患

最大线程数无上限,若任务执行时间过长且提交频繁,会创建大量线程,导致 CPU / 内存耗尽,生产环境需控制任务执行时间。

方法 3:SingleThreadExecutor(单线程线程池)

特点
  • 核心线程数 = 1,最大线程数 = 1(只有 1 个工作线程)。
  • 任务队列是无界队列(LinkedBlockingQueue),任务按 FIFO 顺序执行。
  • 适用于:需要串行执行任务的场景(如日志写入、订单处理,避免并发冲突)。
代码实现
java 复制代码
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SingleThreadExecutorDemo {
    public static void main(String[] args) {
        // 1. 创建单线程线程池(只有1个工作线程)
        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

        // 2. 提交5个任务(串行执行,前一个完成才执行下一个)
        for (int i = 1; i <= 5; i++) {
            singleThreadExecutor.submit(new MyTask(i));
        }

        // 3. 关闭线程池
        singleThreadExecutor.shutdown();
    }
}
执行结果(关键观察)
  • 只有 1 个线程(pool-1-thread-1),5 个任务按顺序串行执行。
java 复制代码
pool-1-thread-1 执行任务 1
pool-1-thread-1 完成任务 1
pool-1-thread-1 执行任务 2
pool-1-thread-1 完成任务 2
pool-1-thread-1 执行任务 3
pool-1-thread-1 完成任务 3
pool-1-thread-1 执行任务 4
pool-1-thread-1 完成任务 4
pool-1-thread-1 执行任务 5
pool-1-thread-1 完成任务 5
底层原理(简化)
java 复制代码
// Executors.newSingleThreadExecutor() 底层代码
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService(
        new ThreadPoolExecutor(
            1, 1,  // 核心线程数=1,最大线程数=1
            0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>() // 无界队列
        )
    );
}
注意隐患

无界队列可能导致任务堆积,触发 OOM,生产环境需自定义队列大小。

方法 4:ScheduledThreadPool(定时 / 周期性线程池)

特点
  • 核心线程数固定,最大线程数 =Integer.MAX_VALUE
  • 支持定时执行 (延迟一段时间后执行)和周期性执行(每隔一段时间重复执行)。
  • 任务队列是延迟队列(DelayedWorkQueue),按任务延迟时间排序。
  • 适用于:定时任务(如定时备份数据)、周期性任务(如每隔 5 分钟检查服务状态)。
代码实现(两种核心用法)
java 复制代码
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ScheduledThreadPoolDemo {
    public static void main(String[] args) {
        // 1. 创建定时线程池(核心线程数=2)
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(2);

        // 用法1:定时执行(延迟2秒后执行一次任务)
        System.out.println("当前时间:" + System.currentTimeMillis() / 1000);
        scheduledThreadPool.schedule(
            new MyTask(1), // 任务
            2,            // 延迟时间
            TimeUnit.SECONDS // 时间单位
        );

        // 用法2:周期性执行(延迟1秒后,每隔3秒执行一次任务)
        // 注意:周期是"上一次任务结束到下一次任务开始"的间隔
        scheduledThreadPool.scheduleAtFixedRate(
            new MyTask(2), // 任务
            1,            // 初始延迟时间
            3,            // 周期时间
            TimeUnit.SECONDS // 时间单位
        );

        // 3. (可选)运行一段时间后关闭线程池(避免无限循环)
        try {
            Thread.sleep(10000); // 主线程等待10秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        scheduledThreadPool.shutdown();
    }
}
执行结果(关键观察)
  • 任务 1:延迟 2 秒后执行一次。

  • 任务 2:延迟 1 秒后执行,之后每隔 3 秒执行一次(共执行 3 次,因主线程等待 10 秒)。

    当前时间:1730000000
    pool-1-thread-1 执行任务 2 // 延迟1秒后执行
    pool-1-thread-1 完成任务 2
    pool-1-thread-2 执行任务 1 // 延迟2秒后执行
    pool-1-thread-2 完成任务 1
    pool-1-thread-1 执行任务 2 // 上一次完成后隔3秒(总4秒)
    pool-1-thread-1 完成任务 2
    pool-1-thread-1 执行任务 2 // 再隔3秒(总7秒)
    pool-1-thread-1 完成任务 2

底层原理(简化)
java 复制代码
// Executors.newScheduledThreadPool(corePoolSize) 底层代码
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}

// ScheduledThreadPoolExecutor 构造方法(继承 ThreadPoolExecutor)
public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(
        corePoolSize,
        Integer.MAX_VALUE,  // 最大线程数无上限
        0, TimeUnit.NANOSECONDS,
        new DelayedWorkQueue() // 延迟队列
    );
}
注意事项
  • scheduleAtFixedRate:周期是 "上一次任务开始到下一次任务开始" 的间隔(若任务执行时间超过周期,会立即执行下一次)。
  • scheduleWithFixedDelay:周期是 "上一次任务结束到下一次任务开始" 的间隔(本文示例用此方法,更常用)。

五、生产环境推荐:自定义线程池(避免默认方法隐患)

Executors的 4 种方法存在隐患(无界队列 OOM、最大线程数无上限),阿里 Java 开发手册明确推荐:使用ThreadPoolExecutor手动创建线程池,自定义核心参数和队列大小。

代码实现(带拒绝策略、线程工厂)
java 复制代码
import java.util.concurrent.*;

public class CustomThreadPoolDemo {
    public static void main(String[] args) {
        // 1. 自定义线程工厂(可选:设置线程名称、优先级等)
        ThreadFactory threadFactory = new ThreadFactory() {
            private int count = 1;

            @Override
            public Thread newThread(Runnable r) {
                Thread thread = new Thread(r);
                thread.setName("custom-thread-" + count++); // 自定义线程名
                thread.setPriority(Thread.NORM_PRIORITY); // 正常优先级
                return thread;
            }
        };

        // 2. 自定义拒绝策略(可选:任务队列满+线程数达最大时的处理逻辑)
        RejectedExecutionHandler rejectedHandler = new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                // 自定义处理:如记录日志、返回友好提示
                System.out.println("任务 " + ((MyTask) r).taskId + " 被拒绝(队列满+线程数达最大)");
            }
        };

        // 3. 创建自定义线程池(核心参数按需配置)
        ThreadPoolExecutor customThreadPool = new ThreadPoolExecutor(
            2,                  // 核心线程数:2
            4,                  // 最大线程数:4(核心2+临时2)
            30,                 // 临时线程空闲超时:30秒
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(3), // 任务队列:有界队列(容量3)
            threadFactory,      // 自定义线程工厂
            rejectedHandler     // 自定义拒绝策略
        );

        // 4. 提交10个任务(核心2+临时2=4线程,队列3,共能处理7个任务,剩余3个被拒绝)
        for (int i = 1; i <= 10; i++) {
            customThreadPool.submit(new MyTask(i));
        }

        // 5. 关闭线程池
        customThreadPool.shutdown();
    }
}
执行结果(关键观察)
  • 线程名是自定义的custom-thread-1~4

  • 队列容量 3,最大线程数 4,共能处理 7 个任务(4 线程 + 3 队列),任务 8、9、10 被拒绝。

    custom-thread-1 执行任务 1
    custom-thread-2 执行任务 2
    custom-thread-3 执行任务 3
    custom-thread-4 执行任务 4
    任务 8 被拒绝(队列满+线程数达最大)
    任务 9 被拒绝(队列满+线程数达最大)
    任务 10 被拒绝(队列满+线程数达最大)
    custom-thread-1 完成任务 1
    custom-thread-1 执行任务 5(队列第1个任务)
    custom-thread-2 完成任务 2
    custom-thread-2 执行任务 6(队列第2个任务)
    custom-thread-3 完成任务 3
    custom-thread-3 执行任务 7(队列第3个任务)
    ...(后续任务完成)

核心参数配置建议(生产环境)
  • 核心线程数:CPU 密集型任务(如计算)→ 核心数 + 1;IO 密集型任务(如数据库查询、网络请求)→ 核心数 ×2。
  • 队列:用有界队列(如ArrayBlockingQueue),避免 OOM,容量按需配置(如 100~1000)。
  • 拒绝策略:根据业务选择(如AbortPolicy抛出异常、CallerRunsPolicy由提交线程执行、DiscardOldestPolicy丢弃最旧任务)。

六、总结

1. 四种创建方法对比
方法 核心线程数 最大线程数 任务队列 适用场景 隐患
FixedThreadPool n n 无界队列 任务量稳定、控制并发度 无界队列 OOM
CachedThreadPool 0 Integer.MAX_VALUE 同步队列 短任务、并发波动大 线程过多导致资源耗尽
SingleThreadExecutor 1 1 无界队列 串行执行任务 无界队列 OOM
ScheduledThreadPool coreSize Integer.MAX_VALUE 延迟队列 定时 / 周期性任务 任务过多导致资源耗尽
2. 核心建议
  • 入门 / 简单场景:用Executors的四种方法(快速开发)。
  • 生产环境:用ThreadPoolExecutor自定义线程池(可控、无隐患)。
  • 线程池关闭:优先用shutdown()(优雅关闭),而非shutdownNow()(强制中断任务)。
  • 监控:通过ThreadPoolExecutorgetActiveCount()(活跃线程数)、getQueue().size()(队列任务数)监控线程池状态。
相关推荐
切糕师学AI1 小时前
Lombok 注解 @Slf4j
java·lombok
寻星探路1 小时前
JavaSE重点总结后篇
java·开发语言·算法
EAIReport1 小时前
自动化报告生成产品内嵌OA/BI平台:解决传统报告痛点的技术方案
java·jvm·自动化
Charles_go2 小时前
C#中级8、什么是缓存
开发语言·缓存·c#
松涛和鸣3 小时前
14、C 语言进阶:函数指针、typedef、二级指针、const 指针
c语言·开发语言·算法·排序算法·学习方法
向着光芒的女孩7 小时前
【IDEA】关不了的Proxy Authentication弹框探索过程
java·ide·intellij-idea
Filotimo_7 小时前
Spring Boot 整合 JdbcTemplate(持久层)
java·spring boot·后端
智商低情商凑8 小时前
Go学习之 - Goroutines和channels
开发语言·学习·golang
半桶水专家8 小时前
Go 语言时间处理(time 包)详解
开发语言·后端·golang