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

线程池原理与类型详解

一、线程池核心原理

  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. 增强系统稳定性

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

相关推荐
用户83071968408214 分钟前
Java IO三大模型(BIO/NIO/AIO)超详细总结
java
sheji341615 分钟前
【开题答辩全过程】以 基于SSM的花店销售管理系统为例,包含答辩的问题和答案
java
Mr_sun.26 分钟前
Day09——入退管理-入住-2
android·java·开发语言
MAGICIAN...37 分钟前
【java-软件设计原则】
java·开发语言
JH307343 分钟前
为什么switch不支持long
java
盐真卿1 小时前
python第八部分:高级特性(二)
java·开发语言
上海合宙LuatOS1 小时前
LuatOS核心库API——【audio 】
java·网络·单片机·嵌入式硬件·物联网·音视频·硬件工程
汤姆yu1 小时前
基于springboot的尿毒症健康管理系统
java·spring boot·后端
TT哇1 小时前
【实习】银行经理端线下领取扫码功能实现方案
java
野犬寒鸦1 小时前
从零起步学习JVM || 第一章:类加载器与双亲委派机制模型详解
java·jvm·数据库·后端·学习