Java并发编程:ThreadPoolExecutor详细源码解析与应用

news/2024/5/7 7:30:20

Thread直接创建线程的弊端

在开始解析ThreadPoolExecutor类之前,让我们先来了解直接创建线程所带来的弊端。在Java中,线程是资源密集型对象,每当需要并发执行任务时,直接创建新线程会带来以下问题:

资源消耗

每个线程都需要分配堆栈内存等资源。在线程数量增多时,资源开销会随之增大,严重时会导致系统性能下降甚至崩溃。

稳定性问题

线程数量无上限地增长,操作系统需要调度的线程数也会无限增加,最终可能导致过度的上下文切换,影响系统的稳定性。

创建和销毁开销

线程的创建和销毁也是需要时间的,频繁的线程创建和销毁会增加系统的负担,降低程序效率。

线程安全

线程并发操作共享资源时,必须通过同步控制来避免竞态条件,这会进一步增加编程的复杂性和运行时开销。

// 直接创建线程案例
public class ThreadCreationDemo {public static void main(String[] args) {for (int i = 0; i < 1000; i++) {Thread t = new Thread(() -> {// 执行任务System.out.println("Task running in a dedicated thread.");});t.start();}}
}

线程池的好处

使用线程池管理并发任务有诸多益处,主要包括以下几点:

资源重用

线程池中的线程在完成任务后不会马上销毁,而是可以循环使用来处理其他任务。这样避免了频繁创建和销毁线程的资源消耗和时间开销。

控制最大并发数

线程池允许我们设置最大并发线程数,不会因为线程数暴增导致资源耗尽,从而保证了系统的稳定性。

提高响应速度

当任务到达时,任务可以不需要等待线程创建就能立即执行,因为线程池中常常有预备的线程准备处理任务。

提供更多功能

线程池提供了定时执行、定期执行、单线程池、可缓存池等不同类型的线程池。这给不同需求的任务执行提供了极大的便利。

任务排队

线程池中不仅包括了线程资源,还有一个任务队列。当所有线程都在忙碌时,新到的任务会放入队列中排队,等待空闲线程资源。

// 使用线程池执行任务的示例
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadPoolDemo {public static void main(String[] args) {// 创建固定大小的线程池ExecutorService executorService = Executors.newFixedThreadPool(10);for (int i = 0; i < 100; i++) {int taskId = i;executorService.submit(() -> {System.out.println("Task " + taskId + " running in thread pool.");});}executorService.shutdown();}
}

线程池基础

线程池(ThreadPool)是管理线程的池子,它可以复用固定的线程,控制并发数量,提高资源利用率。

线程池类结构关系

在Java中,线程池的实现依赖于java.util.concurrent包下的几个关键类。其中,Executor接口定义了执行任务的简单接口,ExecutorService是更完整的异步任务执行框架,ThreadPoolExecutor和ScheduledThreadPoolExecutor是这个框架的具体实现。

// Executor接口和ExecutorService用法示例
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ExecutorExample {public static void main(String[] args) {ExecutorService executorService = Executors.newSingleThreadExecutor();executorService.execute(() -> System.out.println("Task executed via ExecutorService."));executorService.shutdown();}
}

创建线程池常用的类Executors

Executors类提供静态工厂方法来创建不同类型的线程池。例如:

  • newFixedThreadPool(int nThreads): 创建固定大小的线程池。
  • newSingleThreadExecutor(): 创建一个单线程的Executor。
  • newCachedThreadPool(): 创建一个可缓存的线程池,如果线程池的大小超过了处理需求,会自动回收空闲线程。
// Executors创建线程池示例
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(4);
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

线程池实例的几种状态

线程池在其生命周期中有几种状态,包括运行、关闭、停止和整理完成。这些状态的转换主要通过调用shutdown、shutdownNow等方法来实现。
file

合理配置线程池的一些建议

  • 尽量使用有界队列,以避免资源耗尽。
  • 线程池大小应该根据系统资源和任务特性来合理设置。
  • 对于IO密集型任务,可以配置更多的线程。
  • 对于CPU密集型任务,则应该按照CPU核心数来设置池大小。
// 线程池配置建议代码示例
int coreCount = Runtime.getRuntime().availableProcessors();
ExecutorService cpuIntensivePool = Executors.newFixedThreadPool(coreCount);

ThreadPoolExecutor详解

ThreadPoolExecutor是ExecutorService接口实现中功能最为丰富的一个类,提供了创建一个可扩展的线程池的方法。

ThreadPoolExecutor构造方法

ThreadPoolExecutor有几个构造方法,最常用的构造方法包括以下参数配置:

  • corePoolSize:核心线程数。
  • maximumPoolSize:最大线程数。
  • keepAliveTime:非核心线程空闲存活时间。
  • unit:keepAliveTime的时间单位。
  • workQueue:任务队列,用于存放等待执行的任务。
  • threadFactory:线程工厂,用于创建线程。
  • handler:拒绝策略,当线程池和队列都满时如何处理新添加的任务。
// ThreadPoolExecutor构造方法示例
import java.util.concurrent.*;public class ThreadPoolExecutorExample {public static void main(String[] args) {int corePoolSize = 5;int maximumPoolSize = 10;long keepAliveTime = 60;TimeUnit unit = TimeUnit.SECONDS;BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(100);ThreadFactory threadFactory = Executors.defaultThreadFactory();RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,threadFactory,handler);executor.execute(() -> System.out.println("Task executed in ThreadPoolExecutor"));executor.shutdown();}
}

ThreadPoolExecutor提供的启动和停止任务的方法

ThreadPoolExecutor提供了各种用于启动和停止任务的方法,如execute, submit, shutdown, shutdownNow等。

  • execute用于提交不需要返回值的任务。
  • submit用于提交需要返回值的任务。
  • shutdown平缓关闭线程池,不再接受新任务,已提交任务继续执行。
  • shutdownNow试图停止所有正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表。

ThreadPoolExecutor提供的适用于监控的方法

此外,ThreadPoolExecutor还提供了许多方法来监控和管理线程池的状态,比如getPoolSize, getActiveCount, getCompletedTaskCount等。

  • getPoolSize返回线程池中的当前线程数。
  • getActiveCount返回活跃线程的近似数量。
  • getCompletedTaskCount返回已完成执行的近似任务总数。
// ThreadPoolExecutor监控方法示例
// ...(使用上面同样的ThreadPoolExecutor实例)
System.out.println("Current Pool Size: " + executor.getPoolSize());
System.out.println("Active Threads: " + executor.getActiveCount());
System.out.println("Completed Tasks: " + executor.getCompletedTaskCount());

实战案例分析

在复杂的软件系统中,线程池的使用可以大幅提升性能和资源利用率。以下是一个实战案例,演示在高并发场景下如何使用ThreadPoolExecutor进行性能调优。

线程池在实际项目中的应用

假设我们开发一个服务,需要处理各种独立的用户请求,请求类型各异,但处理时间相对较短。使用线程池可以大大减少因频繁创建和销毁线程造成的开销。

// 实际项目中使用ThreadPoolExecutor的示例
import java.util.concurrent.*;public class RequestHandler {private final ThreadPoolExecutor executor;public RequestHandler(int corePoolSize, int maxPoolSize, long keepAliveTime, TimeUnit unit, int queueCapacity) {BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(queueCapacity);this.executor = new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, unit, workQueue);}public void handleRequest(Runnable task) {executor.execute(task);}// 停止线程池的方法,通常在服务停止时调用public void shutdown() {executor.shutdown();}
}

上例中,当请求到达时,通过handleRequest方法将请求封装成任务提交给线程池处理,这样可以有效分流请求,避免服务拥堵。

性能调优实例

为了进一步提高线程池的性能,我们需要根据具体情况调整配置参数。如:

  • 如果任务都是CPU密集型的,可以将线程池的大小设置为CPU核心数量加1。
  • 对于IO密集型任务,线程数可以设置得更多,因为线程经常会处于等待状态。
  • 合理配置任务队列的大小,既可以避免内存占用过高,也能减少响应时间。
// 性能调优示例配置
int corePoolSize = Runtime.getRuntime().availableProcessors() + 1;
int maxPoolSize = corePoolSize * 2;
long keepAliveTime = 60;
TimeUnit unit = TimeUnit.SECONDS;
int queueCapacity = 500;RequestHandler handler = new RequestHandler(corePoolSize, maxPoolSize, keepAliveTime, unit, queueCapacity);

故障排查与优化策略

如果线程池中出现性能瓶颈或异常,我们需要进行故障排查和优化。一些常见的策略包括:

  • 使用监控工具来观察线程池的大小,活动线程数量,队列大小等关键指标。
  • 分析任务执行时间,如果发现任务执行时间过长,检查是否有资源竞争或不合理的同步块。
  • 检查拒绝策略和任务拒绝记录,看是否因线程池饱和导致任务被拒绝。

http://www.mrgr.cn/p/18524721

相关文章

使用 ForAttributeWithMetadataName 提高 IIncrementalGenerator 增量 Source Generator 源代码生成开发效率和性能

本文将告诉大家如何使用 ForAttributeWithMetadataName 方法用来提高 IIncrementalGenerator 增量 Source Generator 源代码生成的开发效率以及提高源代码生成器的运行效率这是一个在 2022 的 6 月 15 才合入的新功能。原因是 Roslyn 团队发现了大量的源代码生成器和分析器项目…

新媒体运营-----短视频运营-----PR视频剪辑----软件基础

新媒体运营-----短视频运营-----PR视频剪辑-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/138079659 文章目录 1.1 PR软件重置与初始化设置1.2 新建项目及序列设置1.3 PR工作区的管理方法1.4 导入4K超高清视频并与ME配合工作1…

Devops基本概念和原理,什么是 DevOps? DevOps 介绍

Devops基本概念和原理,什么是 DevOps? DevOps 介绍Devops基本概念和原理,什么是 DevOps? DevOps 介绍 一、什么是DevOps 1、 DevOps概述 DevOps,即Development and Operations,是一组过程、方法与系统的统称,用于促进软件开发、运维和质量保障部门之间的沟通、协作与整合…

矩阵树定理 BEST 定理

傲慢的青蛙矩阵树定理 \(\text{BEST}\) 定理 证明很复杂,连 \(\text{cmd}\) 这种无敌神犇都不会,而且对定理本身的可扩展性几乎为 \(0\),即每次套用的定理都跟模板一模一样。 矩阵树 无论任何情况,一定要不能有自环 无论任何情况,一定要不能有自环 无论任何情况,一定要不…

T1级,生产环境事故—Shell脚本一键备份K8s的YAML文件

大家好&#xff0c;我叫秋意零。 最近对公司进行日常运维工作时&#xff0c;出现了一个 T1 级别事故。导致公司的“酒云网”APP的无法使用。我和我领导一起搞了一个多小时&#xff0c;业务也停了一个多小时。 起因是&#xff1a;我的部门直系领导&#xff0c;叫我**删除一个 …

深入分析nginx偶尔502错误根因-以及常见错误总结

以架构师的能力标准去分析每个问题&#xff0c;过后由表及里分析问题的本质&#xff0c;复盘总结经验&#xff0c;并把总结内容记录下来。当你解决各种各样的问题&#xff0c;也就积累了丰富的解决问题的经验&#xff0c;解决问题的能力也将自然得到极大的提升。励志做架构师的…

二项式系数

火大二项式系数 更完整的思考过程以及代数推理过程都可以看数学书,所以我尽量给每个式子都赋予组合意义。 因为在有足够强的代数推理能力之前,没有组合意义往往是困难的。 恒等式赋予组合意义往往都是左边右边分开找意义。 常见公式: \[\begin{aligned} \binom{n}{k}&=\…

Verilog2001中bit slicing特性

1、问题来源 HDLBits题目:Mux256to1v 实现256-1且位宽为4的多路选择器 从下图可以发现,直接使用in[ sel4+3 : sel4 ]是不行的,会报错2、两种解决方法利用bit slicing特性 bit slicing特性的内容可以参考link1module top_module( input [1023:0] in,input [7:0] sel,output […

TODO-力扣-661. 图片平滑器

1.题目 题目地址(661. 图片平滑器 - 力扣(LeetCode)) https://leetcode.cn/problems/image-smoother/ 题目描述 图像平滑器 是大小为 3 x 3 的过滤器,用于对图像的每个单元格平滑处理,平滑处理后单元格的值为该单元格的平均灰度。 每个单元格的 平均灰度 定义为:该单元格…

力扣-提交执行报错类型

1.参考 参考链接:提交执行报错 1.1 超出内存限制1.2 数组访问越界1.3 初始化有误1.4 递归基线错误

uniapp H5实现签名

第一种&#xff1a;跳转签名页面 1、创建审核页面audit.vue <template><view><uni-section title""><view class"auditClass"><uni-forms :model"baseFormData" ref"baseFormRef" :rules"rules&quo…

zigbee开发,低功耗,通信加密开发

一。低功耗 1.低功耗应用场景1、不利于更换电池的设备2、手持便携设备3、实时性要求不高的设备 2.低功耗工作原理1、时钟降至最低2、暂时不用的外设关闭、需要在启动3、I/O配置 用电情况可以简化为: 等一会运行一下。 3.zigbee实现低功耗 1.协调器+路由器+终端 补充:CC2530(…

爽了!免费的SSL,还能自动续期!

作者:小傅哥 博客:https://bugstack.cn沉淀、分享、成长,让自己和他人都能有所收获!😄大家好,我是技术UP主小傅哥。 兄弟👬🏻,当你手里有不少域名,每个域名又配置子域名,那么ssl将是一笔不小的费用。当然各个云厂商,也都有提供免费的ssl证书,但这里有一个问题,…

【C++】---STL之list的模拟实现

【C】---STL之list的模拟实现 一、list模拟实现思路二、结点类的实现三、list迭代器的实现1、ListIterator类2、构造函数3、operator*运算符重载5、operator->运算符重载6、operator&#xff01;运算符重载7、operator运算符重载8、前置9、后置10、前置--11、后置-- 四、lis…

华为云Stack8.3面向香港正式发布,六大亮点激发云上跃迁

近日,在华为云香港峰会2024上,华为混合云副总裁胡玉海面向香港市场发布华为云Stack8.3,提供110+本地云服务和六大亮点,帮助中国香港政企持续提升用云深度,激发业务创新。本文分享自华为云社区《华为云Stack8.3面向香港正式发布,六大亮点激发云上跃迁》,作者:华为云头条…

【AIGC调研系列】Bunny-Llama-3-8B-V与其他多模态大模型相比的优劣

Bunny-Llama-3-8B-V作为基于Llama-3的多模态大模型&#xff0c;其优势主要体现在以下几个方面&#xff1a; 性能超越其他模型&#xff1a;根据我搜索到的资料&#xff0c;Bunny-Llama-3-8B-V在多个主流Benchmark上表现良好&#xff0c;超越了LLaVA-7B、LLaVA-13B、Mini-Gemini…

抽象的代理模式1.0版本

前言&#xff1a; 在阅读Spring Security官方文档时&#xff0c;里面设计到了一种设计模式——代理模式Proxy 众里寻她千百度&#xff0c;蓦然回首&#xff0c;那人却在灯火阑珊处 开始 在之前的文章里陈述了一个观点——编程语言和语言没有区别 现看看我们日常生活中的代理…

怎么设置 idea terminal 窗口的编码格式

1 修改Terminal 窗口为 Git bash 窗口 打开 settings 设置界面&#xff0c;选择 Tools 中的 Terminal (File -> settings -> Tools -> Terminal) 修改 Shell path 为你的 Git bash 安装路径&#xff0c;我的在 C:\my_software\java\Git\bin\bash.exe 2 解决中文显示…

python r代表什么意思

r/R&#xff0c;即raw的缩写&#xff0c;意思是未经加工的&#xff1b;自然状态的&#xff1b;未经处理的&#xff1b;未经分析的&#xff1b;原始的。 在Python中r/R表示非转义的原始字符串。与普通字符相比&#xff0c;其他相对特殊的字符&#xff0c;其中可能包含转义字符&…

添加阿里云yum源

添加阿里云yum源 要添加阿里云的 yum 源&#xff0c;可以执行以下步骤&#xff1a; 首先&#xff0c;备份你的现有 yum 源配置文件&#xff0c;以防止意外更改&#xff1a; sudo cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup然后&#xf…