线程池原理及其常用线程池

线程池原理与类型详解

一、线程池核心原理

  1. 为什么需要线程池

• 降低资源消耗:减少线程创建和销毁的开销

• 提高响应速度:任务到达时,线程已存在,直接执行

• 统一管理:控制并发数量,避免系统崩溃

• 提供功能:支持定时、延迟、周期执行

  1. 核心组件
bash 复制代码
// ThreadPoolExecutor 核心参数
public ThreadPoolExecutor(
    int corePoolSize,      // 核心线程数
    int maximumPoolSize,   // 最大线程数  
    long keepAliveTime,    // 空闲线程存活时间
    TimeUnit unit,         // 时间单位
    BlockingQueue<Runnable> workQueue,  // 工作队列
    ThreadFactory threadFactory,        // 线程工厂
    RejectedExecutionHandler handler    // 拒绝策略
)
  1. 工作原理流程

新任务提交

  1. 核心线程是否已满? ──否──→ 创建核心线程执行
    ↓是
  2. 工作队列是否已满? ──否──→ 任务入队等待
    ↓是
  3. 线程数是否达到最大? ──否──→ 创建非核心线程执行
    ↓是
  4. 执行拒绝策略

二、Java 内置线程池类型

  1. FixedThreadPool(固定大小)
bash 复制代码
ExecutorService executor = Executors.newFixedThreadPool(5);

特点:

• 固定数量的核心线程

• 无界队列(LinkedBlockingQueue)

• 线程永不回收

• 适用:负载较重的服务器,需要限制线程数

  1. CachedThreadPool(缓存线程池)
bash 复制代码
ExecutorService executor = Executors.newCachedThreadPool();

特点:

• 核心线程数 = 0,最大线程数 = Integer.MAX_VALUE

• 使用 SynchronousQueue(不存储元素)

• 空闲线程60秒后回收

• 适用:执行大量短期异步任务

  1. SingleThreadExecutor(单线程)
bash 复制代码
ExecutorService executor = Executors.newSingleThreadExecutor();

特点:

• 只有一个线程

• 无界队列

• 保证任务顺序执行

• 适用:需要顺序执行的任务

  1. ScheduledThreadPool(定时线程池)
bash 复制代码
ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);

特点:

• 支持定时、周期性执行

• 核心线程可回收

• 适用:定时任务、延时任务

  1. WorkStealingPool(工作窃取,Java 8+)
bash 复制代码
ExecutorService executor = Executors.newWorkStealingPool();

特点:

• 基于 ForkJoinPool

• 使用工作窃取算法

• 并行处理任务

• 适用:计算密集型任务

三、线程池状态

bash 复制代码
// ThreadPoolExecutor 中的状态定义
private static final int RUNNING    = -1 << COUNT_BITS;  // 运行中
private static final int SHUTDOWN   =  0 << COUNT_BITS;  // 关闭中(不接受新任务)
private static final int STOP       =  1 << COUNT_BITS;  // 停止(不处理队列任务)
private static final int TIDYING    =  2 << COUNT_BITS;  // 整理中
private static final int TERMINATED =  3 << COUNT_BITS;  // 终止

四、拒绝策略

  1. AbortPolicy(默认)
bash 复制代码
// 抛出 RejectedExecutionException
new ThreadPoolExecutor.AbortPolicy()
  1. CallerRunsPolicy
bash 复制代码
// 由调用线程执行任务
new ThreadPoolExecutor.CallerRunsPolicy()
  1. DiscardPolicy
bash 复制代码
// 直接丢弃任务,不抛异常
new ThreadPoolExecutor.DiscardPolicy()
  1. DiscardOldestPolicy
bash 复制代码
// 丢弃队列中最旧的任务,然后重试
new ThreadPoolExecutor.DiscardOldestPolicy()

五、自定义线程池示例

bash 复制代码
// 生产环境推荐自定义线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
    5,                                      // 核心线程数
    10,                                     // 最大线程数
    60L, TimeUnit.SECONDS,                  // 空闲时间
    new ArrayBlockingQueue<>(100),          // 有界队列(防止内存溢出)
    new ThreadFactory() {                   // 自定义线程工厂
        private final AtomicInteger count = new AtomicInteger(1);
        @Override
        public Thread newThread(Runnable r) {
            return new Thread(r, "my-thread-" + count.getAndIncrement());
        }
    },
    new ThreadPoolExecutor.CallerRunsPolicy()  // 拒绝策略
);

六、线程池监控

bash 复制代码
ThreadPoolExecutor executor = ...;

// 监控指标
executor.getPoolSize();          // 当前线程数
executor.getActiveCount();       // 活动线程数
executor.getQueue().size();      // 队列大小
executor.getCompletedTaskCount();// 完成任务数
executor.getTaskCount();         // 总任务数

七、最佳实践

  1. 设置合理的拒绝策略

// 根据业务需求选择

bash 复制代码
new ThreadPoolExecutor.CallerRunsPolicy()  // 重要任务,不能丢弃
new ThreadPoolExecutor.AbortPolicy()       // 快速失败,便于排查
  1. 正确关闭线程池
bash 复制代码
executor.shutdown();                    // 温和关闭
executor.shutdownNow();                 // 立即关闭
executor.awaitTermination(10, TimeUnit.SECONDS);  // 等待关闭
  1. 线程泄漏问题
bash 复制代码
// 使用 submit() - 异常被吞没
executor.submit(() -> {
    try {
        // 任务逻辑
    } catch (Exception e) {
        // 必须捕获异常,否则线程会退出,控制台没有任何异常输出!程序继续运行,就像什么都没发生
        log.error("任务执行异常", e);
    }
});

// 使用 execute() - 异常能抛出
executor.execute(() -> {
    int result = 1 / 0;  // ❌ 异常能抛出到控制台
});

总结

线程池是 Java 并发编程的核心组件,合理使用线程池可以:

  1. 提高系统性能
  2. 控制资源消耗
  3. 提供统一管理
  4. 增强系统稳定性

根据业务场景选择合适的线程池类型和参数配置是关键。

相关推荐
Emily156853598701 小时前
Emerson 1C31129G03 Analog Input Module
java·开发语言·前端·plc·emerson·1c31129g03·input module
就叫_这个吧6 小时前
Java递归方法实现面包屑导航
java·开发语言
fīɡЙtīиɡ ℡6 小时前
AI 应用系统设计
java·开发语言·人工智能
城管不管6 小时前
重生——第十一次面试之挖财一面2026.8.19已OC
java·服务器·jvm·数据库·spring·面试·职场和发展
码匠许师傅6 小时前
【C++ 面试真题】26. 聊聊 C++ 的智能指针
java·c++·面试
AI绘画哇哒哒7 小时前
【建议收藏!】35岁后端血泪忠告,这3类人别硬转Agent(过来人亲述)
java·人工智能·后端·ai·程序员·大模型·agent
最强小杰7 小时前
gpt-5.6-sol 频繁报 503 怎么办?区分容量熔断和限速 429 的排查方法 + 可复用 retry wrapper
java·人工智能·gpt·ai
吠品9 小时前
Wine 在 Linux 上运行 Windows 软件完整指南
java·linux·服务器
亚历克斯神10 小时前
智能搜索系统的升级复盘——从 Elasticsearch 到混合检索的检索质量提升
java·spring·微服务
金銀銅鐵11 小时前
[Java] 一个方法最多可以有多少个入参?
java·jvm