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

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.mzph.cn/bicheng/3443.shtml

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

相关文章

贪吃蛇身子改进加贪吃蛇向右移动

1. 蛇移动的思想&#xff1a; 其实就是删除头节点 &#xff0c;增加尾节点&#xff1b;一句代码搞定 struct Snake *p; p head; head head -> next; free(p) 防止造成多的空间节点 2.增加尾节点代码思想&#xff1a; 2.1 .开辟new 节点的空间 struct Snake *new (stru…

解决Android studio更换sdk地址后flutter项目显示no device selected

问题描述 因为之前sdk的路径在c盘上&#xff0c;经常在更新或下在sdk后c盘饱满&#xff0c;于是就更换了sdk的路径&#xff0c;更换sdk路径后就导致flutter项目在选择设备的时候出现no device selected 找不到设备&#xff0c;但是在device Manager可以看到物理设备或者是虚拟…

深入了解Eureka:微服务架构中的服务发现与注册中心

引言 微服务架构的兴起使得应用程序变得更加模块化和可扩展。在这种架构下&#xff0c;服务发现与注册中心扮演着至关重要的角色。本文将深入探讨Eureka作为服务发现与注册中心的作用、优缺点、重要性以及其服务架构。 一、Eureka的作用 Eureka是Netflix开源的一款用于构建分…

【Quartz】quartz集群模式下数据库表设计原则(详细版)

目录 引言 1. 数据共享性 2. 锁管理 3. 持久性和一致性 4. 状态追踪 5. 元数据管理 6. 优化和索引 主要的Quartz表包括 引言 Quartz 是一个流行的开源作业调度库&#xff0c;广泛用于在Java应用程序中安排作业。在Quartz的集群模式中&#xff0c;多个Quartz实例共享一个…

Java设计模式:使用责任链模式和状态模式优化‘审批流程‘

Java设计模式&#xff1a;使用责任链模式和状态模式优化审批流程 摘要引言 需求流程图正文内容&#x1f4d0; 基本概念介绍 功能实现示例1:设计模式&#xff1a;责任链模式方法&#xff1a;好处&#xff1a; 示例2:设计模式&#xff1a;责任链模式方法和操作流程&#xff1a;好…

【canvas】前端创造的图片粒子动画效果:HTML5 Canvas 技术详解

前端创造的图片粒子动画效果&#xff1a;HTML5 Canvas 技术详解 我们将深入探讨如何通过 HTML5 的 Canvas 功能&#xff0c;将上传的图片转换成引人入胜的粒子动画效果。这种效果将图片分解成小粒子&#xff0c;并在用户与它们交互时产生动态变化。我们将分步骤详细解析代码&a…

EasyRecovery数据恢复软件2025永久免费电脑版下载

EasyRecovery数据恢复软件是一款业界知名的数据恢复工具&#xff0c;它凭借强大的恢复能力和广泛的数据兼容性&#xff0c;帮助用户从各种存储设备中恢复丢失或删除的数据。以下是关于EasyRecovery数据恢复软件的详细介绍。 EasyRecovery绿色破解下载网盘链接: https://pan.ba…

自动驾驶行业源代码防泄漏解决方案

行业背景&#xff1a; 随着新一代信息通信及人工智能技术的快速发展&#xff0c;汽车作为这些新技术应用的重要载体&#xff0c;正在加速向智能化和网联化转型&#xff0c;以自动驾驶研发为主业的企业也越来越多&#xff0c;如何保障自己研发的算法、模型、系统不被研发人员离…

Linux入门攻坚——20、systemd、(sysvinit、upstart重温)

再一次讲到Linux系统启动流程&#xff1a; POST --> Boot Sequence --> Bootloader(grub) --> kernel initramfs(initrd) --> rootfs --> /sbin/init 对于init&#xff0c;即系统内核加载完毕后&#xff08;加载kernel和切换根文件系统&#xff09;运行…

Python项目开发实战:怎么实现端口扫描器

注意:本文的下载教程,与以下文章的思路有相同点,也有不同点,最终目标只是让读者从多维度去熟练掌握本知识点。 下载教程:Python项目开发实战_端口扫描器的实现_编程案例解析实例详解课程教程.pdf 1、步骤 在Python项目开发中,设计并实现一个端口扫描器是一项基础且实用的…

【WebRTC】【Unity】局域网UDP通信为何不通

【背景】 还是在研究Unity中实现VR桌面&#xff0c;希望能够通过UDP广播先找到所有活跃的Client。但是发现UDP广播并未能够成功传递给同一局域网正在运行的客户端。 【分析】 UDP信息在局域网不通可能有如下几个原因&#xff1a; 未连在同一个网段防火墙问题是否存在其它网…

STM32H750外设ADC之开始和结束数据转换功能

目录 概述 1 开始转换 1.1 使能ADSTART 1.2 使能JADSTART 1.3 ADSTART 通过硬件清零 2 转换时序 3 停止正在进行的转换&#xff08; ADSTP、 JADSTP&#xff09; 3.1 停止转换功能实现 3.2 停止转换流程图 概述 本文主要讲述了STM32H750外设ADC之开始和结束数据转换…

CentOS8/RHEL8 root密码破解

我们知道root是CentOS8/RHEL8系统的管理员用户&#xff0c;一般情况下&#xff0c;我们是不会把其密码忘记的&#xff0c;如果万一忘记了&#xff0c;如果破解root密码呢&#xff0c;今天就为大家详细讲讲。 1.CentOS8/RHEL8 root密码破解 1.默认安装及默认配置情况下&#x…

白杨SEO:2024年短视频怎么做?转型做抖音、快手、视频号等短视频流量难吗?怎么做更好?

短视频怎么做&#xff1f; 短视频怎么做&#xff1f;其实这是一个很宽泛的问题&#xff0c;就等同于赚钱怎么赚更多&#xff0c;一般是纯外行或者小白才会这么问。稍微懂一点会加一些前置或者后置条件&#xff0c; 比如&#xff1a; 抖音短视频流量怎么做&#xff1f; 抖音…

实践遥感场景目标检测,基于YOLOv8全系列【n/s/m/l/x】参数模型开发构建遥感场景下MSTAR数据基础上的目标检测识别系统

遥感相关的实践在我们前面的系列博文中也有相关的一些实践&#xff0c;基于MASTAR数据集开发构建对应的目标检测系统在前文也有一些介绍&#xff0c;感兴趣的话可以自行移步阅读即可&#xff1a; 《基于YOLOv7开发构建MSTAR雷达影像目标检测系统》 《基于yolov5n的轻量级MSTA…

前端开发攻略---用原生JS在网页中也能实现语音识别

1、语音识别的过程 语音识别涉及三个过程&#xff1a;首先&#xff0c;需要设备的麦克风接收这段语音&#xff1b;其次&#xff0c;语音识别服务器会根据一系列语法 (基本上&#xff0c;语法是你希望在具体的应用中能够识别出来的词汇) 来检查这段语音&#xff1b;最后&#xf…

Git操作与异常处理

文章目录 常用操作1、代码拉取2、代码提交3、暂存区状态4、提交代码5、推送远程仓库 异常处理【1】报错信息&#xff1a;Cannot pull into a repository with state: MERGING【2】报错信息&#xff1a;You have not concluded your merge (MERGE_HEAD exists)【3】报错信息&…

【网络编程】网络编程概念 | TCP和UDP的区别 | UDP数据报套接字编程 | Socket

文章目录 网络编程一、什么是网络编程1.TCP和UDP的区别 二、UDP数据报套接字编程DatagramSocketDatagramPacket回显服务器&#xff08;echo server&#xff09; 网络编程 一、什么是网络编程 通过网络&#xff0c;让两个主机之间能够进行通信。基于通信来完成一定的功能。 ​…

2.7设计模式——Proxy 代理模式(结构型)

意图 为其它对象提供一种代理以控制这个对象的访问。 结构 Proxy保存一个引用使得代理可以访问实体&#xff1b;提供一个与Subject的接口相同的接口&#xff0c;使代理可以用来替代实体&#xff1b;控制实体的存取&#xff0c;并可能负责创建和删除它&#xff1b;其他功能依赖…

STCAD转换 晶联讯1353(5VLCD)显示

/***晶联讯1353(5VLCD)显示调节电位器参数变化***/ /******2018 6 30 08:50*L252 CODE 1339 ******/ /***变频器 PWM2017 5 6板测试AD晶联讯1353*****/ #include <reg52.h> // #define uint unsigned int …