Java线程池

2709 字
14 分钟
Java线程池

总体设计#

ThreadPoolExecutor实现的顶层接口是Executor,顶层接口Executor提供了一种思想:将任务提交和任务执行进行解耦。用户无需关注如何创建线程,如何调度线程来执行任务,用户只需提供Runnable对象,将任务的运行逻辑提交到执行器(Executor)中,由Executor框架完成线程的调配和任务的执行部分。ExecutorService接口增加了一些能力:(1)扩充执行任务的能力,补充可以为一个或一批异步任务生成Future的方法;(2)提供了管控线程池的方法,比如停止线程池的运行。AbstractExecutorService则是上层的抽象类,将执行任务的流程串联了起来,保证下层的实现只需关注一个执行任务的方法即可。最下层的实现类ThreadPoolExecutor实现最复杂的运行部分,ThreadPoolExecutor将会一方面维护自身的生命周期,另一方面同时管理线程和任务,使两者良好的结合从而执行并行任务。

线程池的核心类:ThreadPoolExecutor#

Java 提供的线程池主要通过 java.util.concurrent.ThreadPoolExecutor 实现。

public ThreadPoolExecutor( int corePoolSize, // 核心线程数(常驻) int maximumPoolSize, // 最大线程数 long keepAliveTime, // 非核心线程的存活时间 TimeUnit unit, // 上面参数的单位 BlockingQueue<Runnable> workQueue, // 任务队列 ThreadFactory threadFactory, // 线程工厂(创建线程的方式) RejectedExecutionHandler handler // 拒绝策略)

线程池参数详解#

参数说明
corePoolSize核心线程数,线程池中始终保留的线程数量。
maximumPoolSize最大线程数。线程池能创建的最大线程数(包括核心线程)。
keepAliveTime非核心线程的空闲存活时间。超过这个时间会被销毁。
unit存活时间单位,如 TimeUnit.SECONDS。
workQueue任务缓存队列(如 ArrayBlockingQueue、LinkedBlockingQueue)。
threadFactory自定义线程创建方式,可设置线程名、守护线程等。
handler拒绝策略,线程池已满时的处理方式。

任务执行流程#

  1. 若线程数 < corePoolSize → 创建新线程执行任务
  2. 若线程数 ≥ corePoolSize → 放入队列
  3. 若队列满且线程数 < maximumPoolSize → 创建新线程执行任务
  4. 若线程数 ≥ maximumPoolSize 且队列也满 → 触发拒绝策略

常见线程池拒绝策略(RejectedExecutionHandler#

策略含义
AbortPolicy默认策略,抛出 RejectedExecutionException 异常。
CallerRunsPolicy谁提交谁执行(任务回退给主线程)。
DiscardPolicy直接丢弃任务,不抛异常。
DiscardOldestPolicy丢弃队列最旧的任务,尝试执行当前任务。

常用线程池队列(BlockingQueue#

队列类型特点
ArrayBlockingQueue有界,数组实现,必须设置容量,适合固定大小任务量
LinkedBlockingQueue可设容量(默认无限),链表实现,适合任务突发
SynchronousQueue不缓存任务,直接传递给线程,适合高并发、立即处理任务

如何创建线程池?

推荐:使用 ThreadPoolExecutor,避免 Executors 工厂方法(不推荐)

ExecutorService threadPool = new ThreadPoolExecutor( 2, 5, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());

不推荐的 Executors(会导致OOM):

Executors.newFixedThreadPool(10); // 队列无界Executors.newCachedThreadPool(); // 线程无限Executors.newSingleThreadExecutor(); // 只有一个线程

线程池使用示例#

public class ThreadPoolDemo { public static void main(String args) { ExecutorService pool = new ThreadPoolExecutor( 2, 4, 10, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy() ); for (int i = 0; i < 10; i++) { final int taskId = i; pool.execute(() -> { System.out.println("任务 " + taskId + " 执行线程: " + Thread.currentThread().getName()); }); } pool.shutdown(); // 关闭线程池 }}

阿里巴巴线程池规范(强烈建议背熟)#

使用 ThreadPoolExecutor 自定义线程池,禁止使用 Executors 提供的默认线程池,原因是:

  • FixedThreadPoolSingleThreadPool 使用的是无界队列,可能导致 OOM;
  • CachedThreadPool 使用的是无界线程数量,也可能 OOM;
  • 推荐设置合理 队列大小最大线程数

Executors#

Executors 是 Java 提供的线程池工厂工具类,位于 java.util.concurrent.Executors 包中,它提供了一些快速创建线程池、定时任务线程池、单线程执行器的方法,使用简单,但也有一些坑(如无界队列、OOM 等问题)。

Executors 常用方法总览

方法名描述推荐程度
newFixedThreadPool(int nThreads)固定线程数线程池❌ 不推荐(使用无界队列)
newCachedThreadPool()缓存线程池,线程数不限制❌ 不推荐(线程数无界)
newSingleThreadExecutor()单线程线程池❌ 不推荐(无界队列)
newScheduledThreadPool(int corePoolSize)支持定时/周期执行的线程池✅ 合理使用
newSingleThreadScheduledExecutor()单线程定时线程池✅ 可用于简单定时任务
newWorkStealingPool() (Java 8+)ForkJoinPool 支持的多任务窃取线程池✅ 特定并行任务
callable(Runnable task, V result)把 Runnable 转为 Callable✅ 工具方法
unconfigurableExecutorService(...)包装不可配置的线程池✅ 安全场景用
privilegedThreadFactory()获取当前线程上下文权限的 ThreadFactory✅ 高安全场景
defaultThreadFactory()默认线程工厂(可用于手动 ThreadPoolExecutor)✅ 常用

重点方法说明

1. newFixedThreadPool(int nThreads)

ExecutorService pool = Executors.newFixedThreadPool(5);
  • 特点:线程数固定,超出部分任务排队执行
  • 使用:适合任务量固定、不会突发堆积的场景
  • ⚠️ 问题:使用的是无界队列,易造成内存堆积 ➜ ❌不推荐

2. newCachedThreadPool()

ExecutorService pool = Executors.newCachedThreadPool();
  • 特点:线程数量无限制(Integer.MAX_VALUE)
  • 使用:适合大量、短周期的轻量任务
  • ⚠️ 问题:线程无限增长,易 OOM ➜ ❌不推荐

3. newSingleThreadExecutor()

ExecutorService pool = Executors.newSingleThreadExecutor();
  • 特点:只有一个线程,串行执行任务
  • 使用:任务必须顺序执行场景
  • ⚠️ 问题:队列无界,线程挂了会自动创建新线程,可能隐藏问题 ➜ ❌不推荐用于生产高并发

4. newScheduledThreadPool(int corePoolSize)

ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
  • 特点:支持定时、周期性任务,基于 ScheduledThreadPoolExecutor
  • 使用:定时任务(如日志归档、缓存刷新)
  • ✅ 推荐使用,但建议配合 ThreadFactory + 拒绝策略显式构造更安全

5. newWorkStealingPool()

ExecutorService pool = Executors.newWorkStealingPool(); // Java 8+

✅ 高性能,但不适合高并发请求处理,且无法精细控制线程池参数

特点:基于 ForkJoinPool,自动按 CPU 核心数创建工作线程

使用:适合并行分治任务(如大数据分片处理)

推荐做法(线程池最佳实践)

阿里巴巴 Java 开发手册强烈建议不要使用 Executors,而是手动构造:

ThreadPoolExecutor pool = new ThreadPoolExecutor( 10, 20, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(200), Executors.defaultThreadFactory(), // 可替换为自定义命名工厂 new ThreadPoolExecutor.CallerRunsPolicy());

拓展方法

方法说明
Executors.callable(Runnable task)把 Runnable 包装成 Callable(返回 null)
Executors.defaultThreadFactory()返回默认线程工厂,线程名如:pool-1-thread-1
Executors.privilegedThreadFactory()创建带当前 AccessControlContext 的线程
Executors.unconfigurableExecutorService()包装线程池,不允许调用配置方法(如 shutdownNow)

总结:Executors 方法使用建议

方法是否推荐说明
newFixedThreadPool无界队列风险
newCachedThreadPool无界线程数风险
newSingleThreadExecutor无界队列、线程替换隐藏问题
newScheduledThreadPool合理控制线程数
newWorkStealingPool高吞吐并行计算
自定义 ThreadPoolExecutor✅强烈推荐可精细控制每个参数,安全、灵活

ScheduledThreadPoolExecutor#

**ScheduledThreadPoolExecutor** 是 Java 中专门用于执行定时任务周期任务的线程池,它比 Timer 更强大、更安全,是企业级定时任务的首选方案。

ScheduledThreadPoolExecutor 是什么?

ScheduledThreadPoolExecutor 是一个线程池,支持以下几种定时任务调度:

调度类型描述
延迟执行延迟固定时间后执行一次任务
周期执行(固定速率)每隔固定时间开始执行(忽略任务执行时长)
周期执行(固定延迟)每次任务执行完后,延迟固定时间再开始下一次

它是 Executors.newScheduledThreadPool(int corePoolSize) 的底层实现类,继承自 ThreadPoolExecutor


创建线程池的方式

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);

等价于:

ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor( 2, Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());

常用方法介绍

1. schedule(Runnable command, long delay, TimeUnit unit)

延迟固定时间后执行一次

scheduler.schedule(() -> { System.out.println("延迟 3 秒后执行一次任务");}, 3, TimeUnit.SECONDS);

2. scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)

周期执行(固定速率):每隔 period 时间触发一次任务,不管任务执行多久

scheduler.scheduleAtFixedRate(() -> { System.out.println("每 5 秒执行一次,固定速率");}, 1, 5, TimeUnit.SECONDS);

✅ 适合:定时轮询任务(比如定时监控、健康检查)

⚠️ 注意:如果任务执行超过周期,会出现任务并发执行重叠


3. scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)

周期执行(固定延迟):每次任务执行完之后,等 delay 时间再执行下一次

scheduler.scheduleWithFixedDelay(() -> { System.out.println("每次执行完后再等 5 秒执行");}, 1, 5, TimeUnit.SECONDS);

✅ 适合:任务耗时不固定、希望每次之间有固定“间隔”


含义和区别:

方法重复?延迟/周期点执行时机并发执行
schedule(Runnable, delay)否(一次性)延迟 delay 后执行一次不管任务执行多长时间,到达延迟点就执行一次不会重复,也不存在并发问题
scheduleAtFixedRate(Runnable, initialDelay, period)是(固定速率)以调度开始时间为基准,每隔 period 安排一次上一次开始时间 + period 到点就触发不会并发执行(内部串行),但如果执行慢于 period,会“立即”补跑
scheduleWithFixedDelay(Runnable, initialDelay, delay)是(固定延迟)以上一次结束时间为基准,结束后再延迟 delay上一次完成时间 + delay 到点才触发同样不并发执行

与 Timer 的区别(为什么用 ScheduledThreadPoolExecutor)

比较点TimerScheduledThreadPoolExecutor
错误隔离❌ 一个任务出异常会终止所有任务✅ 一个任务异常不影响其他任务
并发支持❌ 单线程✅ 多线程
精度一般更精确
线程管理❌ 不支持✅ 支持线程回收、拒绝策略等

✅ 所以:推荐使用 ScheduledThreadPoolExecutor 替代 Timer


推荐封装方式(更安全、更灵活)

ScheduledThreadPoolExecutor scheduler = new ScheduledThreadPoolExecutor( 4, new BasicThreadFactory.Builder() .namingPattern("scheduled-task-%d") .daemon(true) .build(), new ThreadPoolExecutor.AbortPolicy());
  • BasicThreadFactory 是来自 commons-lang3 的工具类,可设置线程名、是否守护线程
  • 可结合监控系统收集执行时间、失败次数等

注意事项

问题建议
任务执行时间过长建议使用 scheduleWithFixedDelay 避免任务重叠
出现异常未捕获用 try-catch 包裹任务逻辑,避免线程退出
执行慢造成堆积可使用 ScheduledExecutorService.shutdownNow() 停止并清理
定时任务过多拆分多个线程池,按业务划分执行优先级

实战示例:每晚 2 点定时执行

public class DailyTask { public static void main(String args) { ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); Runnable task = () -> { System.out.println("执行每日任务:" + LocalDateTime.now()); }; long initialDelay = computeInitialDelay(); // 计算当前时间到 2 点的间隔 long period = 24 * 60 * 60; // 每 24 小时执行一次 scheduler.scheduleAtFixedRate(task, initialDelay, period, TimeUnit.SECONDS); } private static long computeInitialDelay() { LocalDateTime now = LocalDateTime.now(); LocalDateTime nextRun = now.withHour(2).withMinute(0).withSecond(0); if (now.compareTo(nextRun) > 0) { nextRun = nextRun.plusDays(1); } return Duration.between(now, nextRun).getSeconds(); }}

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

Java线程池
https://muxui.com/posts/java线程池/
作者
Muxui
发布于
2025-06-23
许可协议
CC BY-NC-SA 4.0
相关文章智能推荐
1
Java线程知识
Java线程基础 Java线程是什么? 线程 (Thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。 进程 (Process):程序运行的实例,每个进程有独立的内存空间。 线程 :进程内部的执行单元,可以
2
自建 Headscale + DERP 全流程实战记录
文章记录一次完整、可上线、可长期运行的 Headscale + 自建 DERP 搭建过程。 本文不是“能跑就行”的教程,而是 生产可用、已多客户端验证 的配置方案。 简介 Tailscale(Headscale)就是组建一个大的局域网,可以将你
3
YOLO训练麻将(Mahjong)识别,导出ONNX
文章本文基于 Roboflow Universe 数据集(例如 $1 )与仓库内脚本,在 Windows 上完成训练,并导出 ONNX 供后端推理。下文配图均为本机实测截图,便于对照。快速体验 小程序 1\. 环境要求 \ Windows 10
4
MyBatisCodeHelper-Pro破解[V3.4.4]
Java提前申明:这篇文章只用于作者个人记录,任何人不得转载、分享。不转发不扩散,如因此造成的任何损失或其他影响,本人概不承担。MyBatisCodeHelper Pro 是一款很不错的 IDEA 插件,有能力的还是购买正版授权。如官方侵权或产生其
5
[开源]AI Summary - WordPress智能摘要生成插件
PHPAI Summary是一款专为WordPress设计的智能摘要生成插件,它集成了五大主流AI服务:百度文心一言、OpenAI ChatGPT、Google Gemini、字节豆包和阿里通义千问。无论您是个人博客作者还是企业网站运营者,这款插
随机文章随机推荐

评论区

Profile Image of the Author
Muxui
Hello, I'm Muxui.
公告
欢迎来到我的博客!这是一则示例公告。
分类
标签
站点统计
文章
24
分类
8
标签
19
总字数
53,213
运行时长
0
最后活动
0 天前
站点信息
构建平台
Local
博客版本
Firefly v6.14.5
文章许可
CC BY-NC-SA 4.0