Java 线程池原理详解

Java 线程池原理详解

一、引言

在高并发场景下,频繁地创建与销毁线程将带来极大的性能开销。为了提升资源复用性与程序响应速度,Java 提供了线程池机制(java.util.concurrent 包)。线程池通过复用线程、控制线程数量、任务排队管理等手段,有效提升了系统稳定性和执行效率。

本文将从原理、结构、核心参数、运行机制、自定义线程池及源码分析等方面,全面解读 Java 线程池的底层设计与使用方法。

👉Java高并发实战

二、线程池的核心思想

线程池的本质是一个线程的复用管理容器,它包含以下几个核心组成部分:

  1. 线程工作线程集合(Workers):由若干线程组成,用于执行提交的任务。
  2. 任务队列(BlockingQueue):用于缓存提交但尚未执行的任务。
  3. 调度策略:根据线程数和队列状态决定任务如何调度。
  4. 拒绝策略(RejectedExecutionHandler):线程池无法处理任务时的应对措施。

三、ThreadPoolExecutor 结构剖析

Java 中线程池的核心类是 ThreadPoolExecutor,它实现了 ExecutorService 接口。其构造函数如下:

public ThreadPoolExecutor(int corePoolSize,                         // 核心线程数int maximumPoolSize,                      // 最大线程数long keepAliveTime,                       // 非核心线程最大存活时间TimeUnit unit,                            // 存活时间单位BlockingQueue<Runnable> workQueue,        // 任务阻塞队列ThreadFactory threadFactory,              // 线程工厂(命名线程)RejectedExecutionHandler handler          // 拒绝策略
)

参数说明:

参数说明
corePoolSize常驻核心线程数,始终存活,优先执行任务
maximumPoolSize最大线程数,任务激增时创建新线程,不能超过此值
keepAliveTime非核心线程在空闲多久后被回收
workQueue用于缓存任务的阻塞队列,如 ArrayBlockingQueueLinkedBlockingQueue
threadFactory用于定制线程名、优先级,利于排查
handler当线程数与队列满时的任务处理策略

四、线程池的任务执行流程

             提交任务↓corePoolSize 是否已满?/           \否             是↓               ↓创建核心线程     队列是否已满?/     \否       是↓          ↓放入任务队列      maximumPoolSize 是否已满?/     \否       是↓          ↓创建非核心线程    启动拒绝策略

五、线程池的类型(Executors 提供的工厂方法)

工厂方法描述
Executors.newFixedThreadPool(n)固定线程数,任务多时放入队列
Executors.newCachedThreadPool()缓存线程池,线程空闲自动释放
Executors.newSingleThreadExecutor()单线程池,任务顺序执行
Executors.newScheduledThreadPool(n)支持延时与周期执行
Executors.newWorkStealingPool()Java 8+,工作窃取线程池,支持任务之间的负载均衡

5.1. FixedThreadPool(固定线程池)

  • 特点:线程数量固定,任务超过线程数则排队等待。
  • 适用场景:稳定任务负载,控制最大并发数。
ExecutorService pool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 5; i++) {pool.submit(() -> {System.out.println(Thread.currentThread().getName() + " 正在执行");});
}

输出示例:线程名固定为 pool-1-thread-n。


5.2. CachedThreadPool(可缓存线程池)

  • 特点:线程数可无限扩展,空闲线程 60 秒内复用。
  • 适用场景:大量短期异步任务。
ExecutorService pool = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {pool.submit(() -> {System.out.println(Thread.currentThread().getName() + " 正在执行");});
}

注意:线程数不设上限,可能导致 OOM。


5.3. SingleThreadExecutor(单线程池)

  • 特点:始终只有一个线程,任务按顺序执行。
  • 适用场景:希望所有任务顺序执行的场景。
ExecutorService pool = Executors.newSingleThreadExecutor();
pool.submit(() -> System.out.println("任务1"));
pool.submit(() -> System.out.println("任务2"));

5.4. ScheduledThreadPool(定时/周期线程池)

  • 特点:支持任务延迟与周期性执行。
  • 适用场景:周期性任务(如日志收集、监控等)。
ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
pool.schedule(() -> System.out.println("延迟任务"), 3, TimeUnit.SECONDS);
pool.scheduleAtFixedRate(() -> System.out.println("周期任务"), 2, 1, TimeUnit.SECONDS);

5.5. WorkStealingPool(Java 8+,工作窃取线程池)

  • 特点:支持任务之间的负载均衡(基于 ForkJoinPool)。
  • 适用场景:并行计算。
ExecutorService pool = Executors.newWorkStealingPool();
pool.submit(() -> System.out.println("任务执行中..."));

⚠️ 建议:不要在生产中直接使用 Executors 提供的线程池,因其队列默认无界或线程数无限制,容易导致 OOM。推荐使用 ThreadPoolExecutor 自定义配置。


六、自定义线程池示例(含注释)

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;public class CustomThreadPoolDemo {public static void main(String[] args) {// 自定义线程工厂:命名线程,便于调试ThreadFactory threadFactory = new ThreadFactory() {private final AtomicInteger count = new AtomicInteger(1);public Thread newThread(Runnable r) {return new Thread(r, "my-thread-" + count.getAndIncrement());}};// 创建线程池ThreadPoolExecutor executor = new ThreadPoolExecutor(2,                           // corePoolSize4,                           // maximumPoolSize30,                          // keepAliveTimeTimeUnit.SECONDS,           // 时间单位new ArrayBlockingQueue<>(2),// 有界任务队列threadFactory,              // 线程工厂new ThreadPoolExecutor.AbortPolicy() // 拒绝策略:抛出异常);// 提交 10 个任务for (int i = 1; i <= 10; i++) {final int taskId = i;executor.submit(() -> {System.out.println(Thread.currentThread().getName() + " 执行任务 " + taskId);try {Thread.sleep(2000); // 模拟任务耗时} catch (InterruptedException e) {Thread.currentThread().interrupt();}});}executor.shutdown(); // 关闭线程池}
}

更多请参考:👉 java中自定义线程池最佳实践

七、拒绝策略详解(RejectedExecutionHandler)

当线程池的线程数达到最大值且队列满时,新任务无法执行,此时触发拒绝策略:

策略类行为说明
AbortPolicy抛出 RejectedExecutionException(默认)
CallerRunsPolicy由调用者线程执行任务
DiscardPolicy静默丢弃任务
DiscardOldestPolicy丢弃队列最旧任务,尝试执行新任务

7.1. AbortPolicy(默认)

  • 行为:直接抛出 RejectedExecutionException
  • 适用:对丢失任务不容忍的系统。
ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,new ArrayBlockingQueue<>(1),new ThreadPoolExecutor.AbortPolicy()
);

7.2. CallerRunsPolicy

  • 行为:任务由提交任务的线程(主线程)来执行。
  • 适用:系统负载暂时过高,希望平滑降速。
pool.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 主线程输出任务执行日志
main 正在执行任务10

7.3. DiscardPolicy

  • 行为:静默丢弃任务。
  • 适用:对任务丢失不敏感的系统。
pool.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());

7.4. DiscardOldestPolicy

  • 行为:丢弃队列中最旧的任务,然后尝试提交当前任务。
  • 适用:优先处理新任务的场景。
pool.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());


八、线程池状态源码解析(源码节选)

线程池的运行状态通过 ctl 控制变量控制,高位代表状态,低位代表线程数:

// 状态位高 3 位 + 工作线程数低 29 位
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));// 五种状态
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;

通过位运算,线程池可以精确控制运行状态与线程总数,是 ThreadPoolExecutor 高性能调度的关键设计。


九、常见问题与建议

问题建议
队列无限制导致内存泄漏使用有界队列
线程数量配置不合理根据 CPU 核心数和任务类型调整
拒绝策略默认异常根据业务重要性选择更温和策略
主线程提前退出使用 shutdown()awaitTermination()

十、线程池参数配置建议

类型建议值
CPU 密集型corePoolSize = CPU 核数 + 1
IO 密集型corePoolSize = 2 × CPU 核数
队列容量视内存和负载大小,推荐使用有界队列
拒绝策略结合业务容错需求选用(如 CallerRunsPolicy)

十一、总结

Java 线程池作为并发编程的核心组件,通过线程重用、任务排队和调度策略,有效解决了资源消耗与任务调度难题。理解其工作机制和底层结构,对于构建高性能、高可靠的系统至关重要。

建议: 在实际开发中应合理设置线程池参数,并避免直接使用 Executors 默认线程池,优先使用 ThreadPoolExecutor 实现自定义线程池配置,以保障系统稳定运行。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/diannao/85720.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mybatis入门到精通

一&#xff1a;什么是Mybatis 二&#xff1a;Mybatis就是简化jdbc代码的 三&#xff1a;Mybatis的操作步骤 1&#xff1a;在数据库中创建一个表&#xff0c;并添加数据 我们这里就省略了 2&#xff1a;Mybatis通过maven来导入坐标&#xff08;jar包&#xff09; 3&#xff1a…

化学方程式配平免费API接口教程

接口简介&#xff1a; 根据反应物和生成物配平化学方程式。 请求地址&#xff1a; https://cn.apihz.cn/api/other/hxfcs.php 请求方式&#xff1a; POST或GET。 请求参数&#xff1a; 【名称】【参数】【必填】【说明】 【用户ID】【id】【是】【用户中心的数字ID&#xff…

Spring学习笔记:Spring的基于注解的XML的详细配置

按照刘Java的顺序&#xff0c;应该是从基于XML的DI开始接着上面的关于IoC容器装配。主要介绍学习Spring的XML基于注解的详细配置。 第一步是搭建一个Spring的基础工程&#xff08;maven管理&#xff09;&#xff0c;通过IoC机制获取IoC容器的对象。 创建maven工程并在pom文件…

(四)动手实现多层感知机:深度学习中的非线性建模实战

1 多层感知机&#xff08;MLP&#xff09; 多层感知机&#xff08;Multilayer Perceptron, MLP&#xff09;是一种前馈神经网络&#xff0c;包含一个或多个隐藏层。它能够学习数据中的非线性关系&#xff0c;广泛应用于分类和回归任务。MLP的每个神经元对输入信号进行加权求和…

第十三篇:MySQL 运维自动化与可观测性建设实践指南

本篇重点介绍 MySQL 运维自动化的关键工具与流程&#xff0c;深入实践如何构建高效可观测体系&#xff0c;实现数据库系统的持续稳定运行与故障快速响应。 一、为什么需要 MySQL 运维自动化与可观测性&#xff1f; 运维挑战&#xff1a; 手动备份容易遗漏或失败&#xff1b; …

蜜獾算法(HBA,Honey Badger Algorithm)

2021年由Hashim等人提出&#xff08;论文&#xff1a;Honey Badger Algorithm: A New Metaheuristic Algorithm for Solving Optimization Problems&#xff09;。模拟蜜獾在自然界中的智能捕食行为&#xff0c;属于群体智能优化算法&#xff08;与粒子群PSO、遗传算法GA同属一…

Duix.HeyGem:以“离线+开源”重构数字人创作生态

在AI技术快速演进的今天,虚拟数字人正从高成本、高门槛的专业领域走向大众化应用。Duix.HeyGem 数字人项目正是这一趋势下的杰出代表。该项目由一支拥有七年AI研发经验的团队打造,通过放弃传统3D建模路径,转向真人视频驱动的AI训练模型,成功实现了低成本、高质量、本地化的…

HTTP常见的请求方法、响应状态码、接口规范介绍

HTTP&#xff08;Hypertext Transfer Protocol&#xff09;是Web通信的基础协议&#xff0c;用于客户端和服务器之间的请求和响应。本文将详细介绍HTTP常见的请求方法、响应状态码以及接口规范&#xff0c;帮助开发者更好地理解和使用HTTP协议。 一、HTTP请求方法 HTTP请求方…

基于Matlab实现LDA算法

线性判别分析&#xff08;Linear Discriminant Analysis, LDA&#xff09;是一种经典的统计方法&#xff0c;常用于特征降维和分类问题。在机器学习领域&#xff0c; 一、LDA基本原理 LDA的目标是寻找一个投影空间&#xff0c;使得类间距离最大化&#xff0c;同时保持类内距离…

matlab基于GUI实现水果识别

基于GUI实现水果识别系统&#xff0c;限一个图片内存在一种水果 图像处理是一种利用计算机分析图像以达到预期结果的技术。图像处理一般指数字图像处理&#xff0c;而数字图像指由工业相机、摄像机、扫描仪等设备捕捉到的二维数组&#xff0c;数组中的元素称为像素&#xff0c…

XML 编码:结构化数据的基石

XML 编码:结构化数据的基石 引言 XML(可扩展标记语言)作为互联网上广泛使用的数据交换格式,已经成为结构化数据存储和传输的重要工具。本文旨在深入探讨XML编码的原理、应用场景以及编码规范,帮助读者更好地理解和运用XML。 XML编码概述 1. XML的起源 XML诞生于1998年…

虚拟机无法开启-关掉虚拟化

这个问题我之前解决过&#xff0c;没做笔记&#xff0c;这次记录下&#xff0c;最常见都上开启bois的cpu虚拟化。 其次是启动或关闭功能页面也需要选择&#xff0c;再就是和wsl都冲突问题&#xff0c;就是今天这个问题 您的主机不满足在启用 Hyper-V 或 Device/Credential Gua…

Python数据可视化科技图表绘制系列教程(二)

目录 表格风格图 使用Seaborn函数绘图 设置图表风格 设置颜色主题 图表分面 绘图过程 使用绘图函数绘图 定义主题 分面1 分面2 【声明】&#xff1a;未经版权人书面许可&#xff0c;任何单位或个人不得以任何形式复制、发行、出租、改编、汇编、传播、展示或利用本博…

LeetCode算法题 (搜索二维矩阵)Day18!!!C/C++

https://leetcode.cn/problems/search-a-2d-matrix/description/ 一、题目分析 给你一个满足下述两条属性的 m x n 整数矩阵&#xff1a; 每行中的整数从左到右按非严格递增顺序排列。每行的第一个整数大于前一行的最后一个整数。 给你一个整数 target &#xff0c;如果 ta…

猎板硬金镀层厚度:新能源汽车高压系统的可靠性基石

在新能源汽车的电池管理系统&#xff08;BMS&#xff09;和电机控制器中&#xff0c;硬金镀层厚度直接关系到高压环境下的电气稳定性与使用寿命。猎板针对车载场景开发的耐电迁移方案&#xff08;金层 2.5μm&#xff0c;镍层 8μm&#xff09;&#xff0c;经 150℃/85% RH 高压…

亚马逊站内信规则2025年重大更新:避坑指南与合规策略

亚马逊近期对Buyer-Seller Messaging&#xff08;买家-卖家站内信&#xff09;规则进行了显著收紧&#xff0c;明确将一些曾经的“灰色操作”列为违规。违规操作轻则收到警告&#xff0c;重则导致账户暂停或绩效受限。本文为您全面解析本次规则更新的核心要点、背后逻辑&#x…

WPF可拖拽ListView

1.控件描述 WPF实现一个ListView控件Item子项可删除也可拖拽排序&#xff0c;效果如下图所示 2.实现代码 配合 WrapPanel 实现水平自动换行&#xff0c;并开启拖拽 <ListViewx:Name"listView"Grid.Row"1"Width"300"AllowDrop"True&…

相机--双目立体相机

教程 链接1 教程汇总 立体匹配算法基础概念 视频讲解摄像机标定和双目立体原理 两个镜头。 双目相机也叫立体相机--Stereo Camera&#xff0c;属于深度相机。 作用 1&#xff0c;获取图像特征&#xff1b; 2&#xff0c;获取图像深度信息&#xff1b; 原理 原理和标定 …

Unity3D仿星露谷物语开发59之定制角色衬衫

1、目标 自定义角色衬衫、裤子、手臂颜色。 2、概念 在Assets -> Sprites -> Output Textures下&#xff0c;Customised_farmer为目前角色所用的精灵表。 如果上面是输出纹理&#xff0c;那么输入纹理是什么呢&#xff1f;它位于Assets/Sprites/Sprite Textures/Chara…

【HarmonyOS 5】游戏开发教程

一、开发环境搭建 ‌工具配置‌ 安装DevEco Studio 5.1&#xff0c;启用CodeGenie AI助手&#xff08;Settings → Tools → AI Assistant&#xff09;配置游戏模板&#xff1a;选择"Game"类型项目&#xff0c;勾选手机/平板/折叠屏多设备支持 二、游戏引擎核心架构…