【JUC】线程池

Java 线程池是多线程编程中的核心组件,它通过复用线程来减少线程创建和销毁的开销,提高系统性能和资源利用率。下面从核心概念、工作原理、使用方式和最佳实践几个方面来讲解。

一、为什么需要线程池

不用线程池的问题:

  • 每次任务都 new Thread(),创建/销毁开销大
  • 线程数量无法控制,高并发时可能耗尽系统资源(内存、CPU)
  • 缺乏统一的任务管理和监控手段

线程池的优势:

  • 降低资源消耗:复用已创建的线程
  • 提高响应速度:任务到达时无需等待线程创建
  • 提高可管理性:统一分配、调优和监控

二、核心类:ThreadPoolExecutor

Java 线程池的核心实现是 java.util.concurrent.ThreadPoolExecutor,它的构造方法有 7 个关键参数:

java 复制代码
public ThreadPoolExecutor(
    int corePoolSize,                  // 核心线程数
    int maximumPoolSize,               // 最大线程数
    long keepAliveTime,                // 空闲线程存活时间
    TimeUnit unit,                     // 时间单位
    BlockingQueue<Runnable> workQueue, // 任务队列
    ThreadFactory threadFactory,       // 线程工厂
    RejectedExecutionHandler handler   // 拒绝策略
)

参数详解

参数 说明
corePoolSize 核心线程数,即使空闲也会保留(除非设置 allowCoreThreadTimeOut)
maximumPoolSize 线程池允许的最大线程数
keepAliveTime 超过核心线程数的空闲线程存活时间
workQueue 存放待执行任务的阻塞队列
threadFactory 创建线程的工厂,可自定义线程名、优先级等
handler 队列满且线程数达最大时的拒绝策略

三、任务提交后的处理流程

这是线程池最核心的机制,理解它非常重要:

复制代码
提交任务
   │
   ├─ 核心线程未满? ──是──> 创建核心线程执行
   │        │否
   ├─ 队列未满? ────是──> 任务入队等待
   │        │否
   ├─ 线程数 < max? ─是──> 创建非核心线程执行
   │        │否
   └─> 执行拒绝策略

关键点:只有当队列满了之后,才会创建超出核心线程数的线程。这个顺序容易被误解。

四、四种拒绝策略

当任务无法被接受时(队列满 + 线程数达 max):

  1. AbortPolicy (默认):抛出 RejectedExecutionException
  2. CallerRunsPolicy:由提交任务的线程自己执行(起降速作用)
  3. DiscardPolicy:直接丢弃,不抛异常
  4. DiscardOldestPolicy:丢弃队列中最老的任务,再尝试提交

也可以自定义实现 RejectedExecutionHandler。

五、Executors 提供的快捷工厂(不推荐)

java 复制代码
Executors.newFixedThreadPool(n);      // 固定线程数,队列无界
Executors.newCachedThreadPool();      // 线程数可无限增长
Executors.newSingleThreadExecutor();  // 单线程,队列无界
Executors.newScheduledThreadPool(n);  // 支持定时/周期任务

《阿里巴巴 Java 开发手册》明确禁止使用 Executors 创建线程池,原因:

  • newFixedThreadPool / newSingleThreadExecutor:使用无界队列 LinkedBlockingQueue,任务堆积可能导致 OOM
  • newCachedThreadPool / newScheduledThreadPool:最大线程数为 Integer.MAX_VALUE,可能创建海量线程导致 OOM

推荐手动 new ThreadPoolExecutor(...),明确各参数。

六、线程池大小如何设置

没有固定公式,取决于任务类型:

  • CPU 密集型:核心数 + 1(减少上下文切换)
  • IO 密集型:核心数 × 2,或更多(线程大部分时间在等待 IO)

更精确的估算:

复制代码
线程数 = 核心数 × 目标CPU利用率 × (1 + 等待时间/计算时间)

实际生产中建议结合压测调整,并配合监控。

七、常用方法

java 复制代码
executor.execute(runnable);       // 提交无返回值任务
Future<T> f = executor.submit(callable); // 提交有返回值任务
executor.shutdown();              // 平滑关闭,不再接受新任务
executor.shutdownNow();           // 立即关闭,尝试中断运行中任务
executor.awaitTermination(...);   // 等待终止
executor.getActiveCount();        // 活跃线程数(监控用)
executor.getQueue().size();       // 队列积压数

八、完整示例

java 复制代码
ThreadPoolExecutor executor = new ThreadPoolExecutor(
    4,                              // 核心线程数
    8,                              // 最大线程数
    60L, TimeUnit.SECONDS,          // 空闲存活时间
    new ArrayBlockingQueue<>(100),  // 有界队列
    new ThreadFactory() {           // 自定义线程工厂
        private final AtomicInteger n = new AtomicInteger(1);
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r, "my-pool-" + n.getAndIncrement());
            t.setDaemon(false);
            return t;
        }
    },
    new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);

for (int i = 0; i < 200; i++) {
    executor.execute(() -> {
        System.out.println(Thread.currentThread().getName() + " 执行任务");
    });
}

executor.shutdown();

九、注意事项与最佳实践

  1. 使用有界队列,避免任务无限堆积导致 OOM
  2. 给线程起有意义的名称,便于排查问题(如 jstack)
  3. 合理设置拒绝策略 ,核心业务建议 CallerRunsPolicy 或自定义
  4. 关闭线程池 :应用停止时调用 shutdown(),避免资源泄漏
  5. 监控线程池:活跃线程数、队列大小、完成任务数、拒绝次数
  6. 避免死锁:父任务等待子任务时,若父子共用同一线程池可能饿死
  7. 区分业务隔离:不同业务用不同线程池,避免相互影响

十、延伸

  • ScheduledThreadPoolExecutor:支持延迟和周期任务
  • ForkJoinPool:分治任务,工作窃取算法(Java 8 parallelStream 底层)
  • CompletableFuture:Java 8 异步编排,可指定线程池
相关推荐
小天源1 小时前
智叟云集政策监测技术方案——原文解析、证据提取与可核验摘要
java·服务器·数据库
dwm888888881 小时前
c# 创建使用本地NuGet包
开发语言·c#
yuzhiboyouye1 小时前
我现在得后端状况以及需要做的
java
小羊没烦恼!1 小时前
在.net中读写config文件的各种方法
java·大数据·运维·服务器·前端·数据库·oracle
2601_962885721 小时前
如何用 Python 获取上证指数、沪深300、中证500 等指数行情?
开发语言·python
余额瞒着我当琳1 小时前
哈希表底层原理解析,直接定址法,哈希冲突,负载因子,哈希函数,开放定址法
java·c++·哈希算法·散列表
张张123y1 小时前
后端第六课:Python 语法速通(0 基础入门,AI 应用开发的第一门语言)
开发语言·人工智能·python·ai·后端开发
Want5952 小时前
Python跳动的爱心①
开发语言·python·数学建模
paopaokaka_luck2 小时前
民宿管理系统 (AI民宿总结、协同过滤推荐算法、WebSocket 在线咨询、ECharts 经营分析、房态与结算联动、 民宿预订业务闭环)
java·人工智能·spring boot·websocket·数据分析·echarts·推荐算法