Java多线程与线程池技术详解(九)
面对苦难的态度:《病隙碎笔》“不断的苦难才是不断地需要信心的原因,这是信心的原则,不可稍有更动。”
孤独与心灵的成长:《我与地坛》“孤独的心必是充盈的心,充盈得要流溢出来要冲涌出去,便渴望有人呼应他、收留他、理解他。”
目录
上一篇博客习题讲解
使用ReentrantLock实现生产者-消费者模式
为什么在某些情况下ReentrantLock的表现优于synchronized?
设计一个场景,说明何时应该选择使用读写锁而不是普通的互斥锁
实现一个简单的银行账户类
公平锁与非公平锁
Shutdown() vs ShutdownNow()
Future 和 FutureTask
创建可暂停和恢复所有线程池任务的系统
知识讲解
第9章 Tomcat线程池技术
9.1 自定义 ThreadPoolExecutor
9.2 Tomcat任务队列
9.3 Tomcat任务线程
9.4 Tomcat任务线程工厂
9.5 Tomcat连接器与线程池
9.6 创建 Tomcat 线程池
9.7 Web服务器异步环境
9.8 Web服务器 NIO
9.9 本章习题
上一篇博客习题讲解
Java多线程与线程池技术详解(八)
Java多线程与线程池技术详解(八)-CSDN博客文章浏览阅读428次,点赞19次,收藏8次。如果只有傻瓜才相信梦想,那么就叫我大傻瓜吧!“想走的路不好走,想做人不好做,都说是身不由己,不是废话么。己不由心,身又岂能由己!https://blog.csdn.net/speaking_me/article/details/144394346?spm=1001.2014.3001.5501
使用ReentrantLock实现生产者-消费者模式
生产者-消费者模式是并发编程中的经典问题,它涉及到两个或多个线程之间的协调工作。为了确保数据的一致性和线程的安全性,通常会使用锁机制来控制对共享资源的访问。下面是一段使用ReentrantLock
实现生产者-消费者模式的示例代码:
import java.util.LinkedList; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;public class ProducerConsumerExample {private final int MAX_SIZE = 5;private final LinkedList<Integer> list = new LinkedList<>();private final Lock lock = new ReentrantLock();private final Condition notFull = lock.newCondition();private final Condition notEmpty = lock.newCondition();public void produce(int value) throws InterruptedException {lock.lock();try {while (list.size() == MAX_SIZE) {System.out.println("Buffer is full, waiting...");notFull.await();}list.add(value);System.out.println("Produced: " + value);notEmpty.signalAll();} finally {lock.unlock();}}public Integer consume() throws InterruptedException {lock.lock();try {while (list.isEmpty()) {System.out.println("Buffer is empty, waiting...");notEmpty.await();}Integer value = list.removeFirst();System.out.println("Consumed: " + value);notFull.signalAll();return value;} finally {lock.unlock();}} }
这段代码中,我们创建了一个固定大小的缓冲区,并通过ReentrantLock
和两个Condition
对象(notFull
和notEmpty
)来管理生产和消费的过程。
为什么在某些情况下ReentrantLock的表现优于synchronized?
ReentrantLock
提供了比synchronized
更灵活的功能,例如可以尝试获取锁、支持公平锁、允许锁中断等特性。此外,在高并发场景下,ReentrantLock
的性能可能优于synchronized
,因为它避免了线程进入内核态的阻塞状态。不过需要注意的是,在低并发的情况下,synchronized
的性能表现可能会更好。
设计一个场景,说明何时应该选择使用读写锁而不是普通的互斥锁
假设有一个缓存系统,其中读取操作远远多于写入操作。在这种情况下,如果使用普通的互斥锁,则每次读取时都会阻止其他读取操作的发生,即使它们不会相互影响。而使用读写锁(如ReentrantReadWriteLock
),则可以在没有写入操作发生时允许多个读取操作同时进行,从而提高了系统的并发度和响应速度。
实现一个简单的银行账户类
对于银行账户类,我们可以分别使用synchronized
和ReentrantLock
来保证线程安全。以下是两种实现方式:
使用synchronized
关键字:
public class BankAccountSynchronized {private double balance;public synchronized void deposit(double amount) {// 存款逻辑}public synchronized boolean withdraw(double amount) {// 取款逻辑return true;} }
使用ReentrantLock
:
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;public class BankAccountReentrantLock {private double balance;private final Lock lock = new ReentrantLock();public void deposit(double amount) {lock.lock();try {// 存款逻辑} finally {lock.unlock();}}public boolean withdraw(double amount) {lock.lock();try {// 取款逻辑return true;} finally {lock.unlock();}} }
两者的主要区别在于synchronized
是隐式锁,自动管理锁的获取与释放;而ReentrantLock
需要显式地调用lock()
和unlock()
方法来控制锁的行为。
公平锁与非公平锁
- 公平锁:所有等待线程按照请求锁的顺序获得锁,这有助于防止饥饿现象的发生,但吞吐量较低。
- 非公平锁:允许新到达的线程插队,即有可能跳过已经在等待的线程直接获得锁,这种方式能提高吞吐量,但在极端情况下可能导致部分线程长时间得不到执行机会。
例如,在一个高频交易系统中,为了最大化吞吐量,可以选择使用非公平锁;而在一个任务调度系统中,为了保证每个任务都能得到及时处理,可能更适合采用公平锁。
Shutdown() vs ShutdownNow()
shutdown()
方法会停止接收新的任务并将试图终止所有正在运行的任务,但它不会立即终止已提交的任务。相反,shutdownNow()
将尝试取消所有未开始的任务,并且会中断正在执行的任务。因此,shutdownNow()
更激进,可能会导致一些任务被中途打断,适用于紧急情况下的快速关闭。
Future 和 FutureTask
Future
接口表示异步计算的结果,提供了检查计算是否完成、等待计算完成以及获取结果的方法。FutureTask
是一个实现了Runnable
和Future
接口的具体类,它可以包装一个Callable或Runnable对象,使得可以通过调用其run()
方法启动任务,并通过get()
方法获取结果或等待任务完成。此外,还可以调用cancel(boolean mayInterruptIfRunning)
来尝试取消任务。
创建可暂停和恢复所有线程池任务的系统
要实现这样一个系统,可以考虑为每个任务添加一个状态标志位,用于指示任务是否应该暂停。当接收到暂停指令时,所有任务都将检查自己的状态并根据需要暂停执行。恢复时,再次检查状态以决定是否继续执行。需要注意的是,这种设计可能会引入额外的复杂性,比如如何同步状态变更以及处理潜在的死锁问题。
知识讲解
第9章 Tomcat线程池技术
9.1 自定义 ThreadPoolExecutor
Tomcat的线程池是基于Java的ThreadPoolExecutor
实现的,但为了适应Web服务器的需求,它做了许多定制化处理。在创建自定义的ThreadPoolExecutor
时,可以指定核心线程数(corePoolSize)、最大线程数(maximumPoolSize)、空闲线程存活时间(keepAliveTime)、任务队列(workQueue)等参数。Tomcat中的ThreadPoolExecutor
与标准JDK版本不同,它增加了对提交任务计数的支持,并且在执行任务失败时会尝试将任务重新加入到任务队列中。
// 自定义ThreadPoolExecutor构造函数 public class CustomThreadPoolExecutor extends ThreadPoolExecutor {public CustomThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime, TimeUnit unit,BlockingQueue<Runnable> workQueue) {super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);// 预热所有核心线程prestartAllCoreThreads();} }
9.2 Tomcat任务队列
Tomcat的任务队列并非直接使用JDK提供的阻塞队列,而是使用了一个名为TaskQueue
的类,它是LinkedBlockingQueue
的一个子类。这个队列实现了特殊的逻辑:当线程池中的线程数量小于最大线程数时,它会优先创建新的线程来处理任务而不是将任务放入队列;只有在线程数达到最大值后才会考虑将任务放入队列。
// TaskQueue 类的部分实现 public class TaskQueue extends LinkedBlockingQueue<Runnable> {@Overridepublic boolean offer(Runnable o) {// 如果线程池大小未达到最大,则返回false,表示队列已满if (parent.getPoolSizeNoLock() < parent.getMaximumPoolSize()) {return false;}// 否则调用父类方法添加任务return super.offer(o);} }
9.3 Tomcat任务线程
每个任务线程都是由TaskThreadFactory
创建出来的,它们继承自Thread
类,并且可以根据需要设置线程名称前缀、守护状态以及优先级。这些线程负责从任务队列中取出任务并执行。
// TaskThreadFactory 创建线程的方法 public class TaskThreadFactory implements ThreadFactory {private final String namePrefix;private final boolean daemon;private final int threadPriority;public TaskThreadFactory(String namePrefix, boolean daemon, int threadPriority) {this.namePrefix = namePrefix;this.daemon = daemon;this.threadPriority = threadPriority;}@Overridepublic Thread newThread(Runnable r) {Thread t = new Thread(r, namePrefix + "-" + threadNumber.getAndIncrement());t.setDaemon(daemon);t.setPriority(threadPriority);return t;} }
9.4 Tomcat任务线程工厂
如上所示,TaskThreadFactory
用于创建线程实例,并允许开发者配置线程的名字、是否为守护进程及优先级。
9.5 Tomcat连接器与线程池
Tomcat的连接器(Connector)负责监听客户端请求,并通过线程池分配线程来处理这些请求。根据不同的I/O模型(BIO/NIO/APR),可以选择不同的连接器实现方式。例如,默认情况下NIO模式下使用的NioEndpoint
会创建一个或多个Acceptor线程来接收新连接,并将其交给Poller线程进行读写操作。
9.6 创建 Tomcat 线程池
在Tomcat启动过程中,AbstractEndpoint#createExecutor()
方法会被调用来初始化线程池。这里不仅设置了线程池的基本属性,还预热了所有的核心线程以确保一旦有请求到来就能立即得到处理。
public void createExecutor() {internalExecutor = true;TaskQueue taskqueue = new TaskQueue();TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority());executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(),60L, TimeUnit.SECONDS, taskqueue, tf);taskqueue.setParent((ThreadPoolExecutor) executor); }
9.7 Web服务器异步环境
对于支持异步Servlet的应用程序来说,Tomcat提供了AsyncContext
机制,使得可以在非阻塞的方式下调用业务逻辑,从而提高系统的并发处理能力。
下面是一个简单的例子展示了如何使用AsyncContext
:
protected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {final AsyncContext asyncContext = request.startAsync();asyncContext.start(() -> {try {// 调用业务方法businessMethod(asyncContext.getResponse());asyncContext.complete();} catch (Exception e) {asyncContext.setError(e);asyncContext.complete();}}); }private void businessMethod(HttpServletResponse response) throws Exception {// 模拟长时间运行的任务Thread.sleep(5000);response.getWriter().println("Hello World!"); }
9.8 Web服务器 NIO
Tomcat的NIO实现依赖于Java NIO库,它允许单个线程管理多个套接字连接。这减少了所需的线程数,并提高了性能。NioEndpoint
类包含了对NIO特性的具体实现,包括但不限于选择器(Selector)、通道(Channel)和缓冲区(Buffer)的操作。
// NioEndpoint 中的部分代码片段 @Override protected void startInternal() throws Exception {// 创建并启动Poller线程poller = new Poller();Thread pollerThread = new Thread(poller, getName() + "-Poller");pollerThread.setPriority(threadPriority);pollerThread.setDaemon(true);pollerThread.start();startAcceptorThreads(); }
9.9 本章习题
考虑到篇幅限制,此处不提供完整的练习题目,但是建议读者尝试完成以下任务来加深理解:
- 实现自己的
ThreadPoolExecutor
,并测试其行为。 - 修改
TaskQueue
的行为,使其在某些条件下拒绝接受新任务。 - 使用
AsyncContext
创建一个异步Servlet应用。 - 探索Tomcat源码中关于NIO的具体实现细节。
相关文章:
Java多线程与线程池技术详解(九)
面对苦难的态度:《病隙碎笔》“不断的苦难才是不断地需要信心的原因,这是信心的原则,不可稍有更动。” 孤独与心灵的成长:《我与地坛》“孤独的心必是充盈的心,充盈得要流溢出来要冲涌出去,便渴望有人呼应他…...

【常考前端面试题总结】---2025
React fiber架构 1.为什么会出现 React fiber 架构? React 15 Stack Reconciler 是通过递归更新子组件 。由于递归执行,所以更新一旦开始,中途就无法中断。当层级很深时,递归更新时间超过了 16ms,用户交互就会卡顿。对于特别庞…...
什么是大语言模型(LLM)
1. 什么是大语言模型(LLM)? LLM 是一种基础模型(Foundation Model)的实例。 基础模型的特点: 使用大量未标注的自监督数据进行预训练。通过学习数据中的模式,生成具有普适性和可适应性的输出…...

柚坛工具箱Uotan Toolbox适配鸿蒙,刷机体验再升级
想要探索智能设备的无限可能?Uotan Toolbox(柚坛工具箱)将是您的得力助手。这款采用C#语言打造的创新型开源工具箱,以其独特的设计理念和全面的功能支持,正在改变着用户与移动设备互动的方式。 作为一款面向专业用户的…...
supervisor使用详解
参考文章: Supervisor使用详解 Supervisor 是一个用 Python 编写的客户端/服务器系统,它允许用户在类 UNIX 操作系统(如 Linux)上监控和控制进程。Supervisor 并不是一个分布式调度框架,而是一个进程管理工具&#x…...

win11电源设置在哪里?控制面板在哪里?如何关闭快速启动?
不知道微软咋想的,从win10(win8)开始搞事情,想把windows娱乐化。 娱乐化的特点就是只照顾傻子不考虑专家,系统设置统统藏起来,开机即用——也只能那么用。 搞两套界面做不到吗? win11非常头疼的…...

【论文阅读笔记】One Diffusion to Generate Them All
One Diffusion to Generate Them All 介绍理解 引言二、相关工作三、方法预备知识训练推理实现细节训练细节 数据集构建实验分结论附录 介绍 Paper:https://arxiv.org/abs/2411.16318 Code:https://github.com/lehduong/onediffusion Authors࿱…...

SpringCloud和Nacos的基础知识和使用
1.什么是SpringCloud 什么是微服务? 假如我们需要搭建一个网上购物系统,那么我们需要哪些功能呢?商品中心、订单中心和客户中心等。 当业务功能较少时,我们可以把这些功能塞到一个SpringBoot项目中来进行管理。但是随…...

人工智能技术的深度解析与推广【人工智能的应用场景】
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默, 忍不住分享一下给大家。点击跳转到网站 学习总结 1、掌握 JAVA入门到进阶知识(持续写作中……) 2、学会Oracle数据库入门到入土用法(创作中……) 3、手把…...
md5sum -c
md5sum -c xxx 命令用于验证文件的 MD5 校验和是否匹配。具体来说,-c 选项告诉 md5sum 命令去读取指定文件(通常是一个包含 MD5 校验和的文件),并与实际文件的 MD5 校验和进行比较。 工作原理: 生成校验和文件&#x…...
excel使用笔记
1.工作表1计算工作表2某列的和 假设我们有两个工作表,分别命名为“Sheet1”和“Sheet2”,我们想要求和这两个工作表中A1到A**单元格的数据,可以在任意一个工作表的单元格中输入以下公式: SUM(Sheet1!A1:A10, Sheet2!A1:A10) SUM…...

keepalived+nginx实现web高可用
目录 高可用集群搭建 Keepalived+nginx实现web高可用 一.节点规划 二.基础准备 1.修改主机名 2.关闭防火墙和selinux服务 三.用keepalived配置高可用 1.安装nginx服务 2.修改nginx配置文件 3.启动nginx 4.访问nginx 5.安装keepalived服务 6.编辑配置文件…...
边界层气象:脉动量预报方程展开 | 湍流脉动速度方差预报方程 | 平均湍流动能收支方程推导
写成分量形式 原始式子: ∂ u i ′ ∂ t u ‾ j ∂ u i ′ ∂ x j u j ′ ∂ u ‾ i ∂ x j u j ′ ∂ u i ′ ∂ x j − 1 ρ ‾ ⋅ ∂ p ′ ∂ x i g θ v ′ θ ‾ v δ i 3 f ϵ i j 3 u j ′ v ∂ 2 u i ′ ∂ x j 2 ∂ ( u i ′ u j ′ ‾ ) ∂ x j…...

TOSUN同星TsMaster使用入门——2、使用TS发送报文,使用graphics分析数据等
在第一章里面已经介绍了关于同星工程的创建和最基础的总线分析,接下来看看怎么使用TS发送报文以及图形化分析数据。 目录 一、使用Graphics分析报文信号/变量(对标CANoe Graphics) 二、使用数值窗口统计信号值/变量 三、使用TS发送报文 3…...

【操作系统】实验七:显示进程列表
实验7 显示进程列表 练习目的:编写一个模块,将它作为Linux内核空间的扩展来执行,并报告模块加载时内核的当前进程信息,进一步了解用户空间和内核空间的概念。 7.1 进程 进程是任何多道程序设计的操作系统中的基本概念。为了管理…...

day10 电商系统后台API——接口测试(使用postman)
【没有所谓的运气🍬,只有绝对的努力✊】 目录 实战项目简介: 1、用户管理(8个) 1.1 登录 1.2 获取用户数据列表 1.3 创建用户 1.4 修改用户状态 1.5 根据id查询用户 1.6 修改用户信息 1.7 删除单个用户 1.8 …...
JavaScript ES6+ 语法速通
一、ES6 基础语法 1. let 和 const 声明变量 let:块级作用域,可以重新赋值。const:块级作用域,声明常量,不能重新赋值。 let name Li Hua; name Li Ming; // 可修改const age 21; // age 22; // 报错࿰…...

移动端h5自适应rem适配最佳方案
网页开发中,我们常用的单位有如下几个: px:像素固定,无法适配各分辨率的移动设备em: 该单位受父容器影响,大小为父元素的倍数rem: 因为html根元素大小为16px,所以默认 1rem 16px,rem只受根元素…...

2024年使用 Cython 加速 Python 的一些简单步骤
文章结尾有最新热度的文章,感兴趣的可以去看看。 本文是经过严格查阅相关权威文献和资料,形成的专业的可靠的内容。全文数据都有据可依,可回溯。特别申明:数据和资料已获得授权。本文内容,不涉及任何偏颇观点,用中立态度客观事实描述事情本身 文章有点长,期望您能坚持看…...

EasyExcel设置表头上面的那种大标题(前端传递来的大标题)
1、首先得先引用easyExcel的版本依赖,我那 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.6</version> </dependency> 2、然后得弄直接的实体类,&…...

【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...
CSS | transition 和 transform的用处和区别
省流总结: transform用于变换/变形,transition是动画控制器 transform 用来对元素进行变形,常见的操作如下,它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...
MySQL 8.0 事务全面讲解
以下是一个结合两次回答的 MySQL 8.0 事务全面讲解,涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容,并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念(ACID) 事务是…...
MinIO Docker 部署:仅开放一个端口
MinIO Docker 部署:仅开放一个端口 在实际的服务器部署中,出于安全和管理的考虑,我们可能只能开放一个端口。MinIO 是一个高性能的对象存储服务,支持 Docker 部署,但默认情况下它需要两个端口:一个是 API 端口(用于存储和访问数据),另一个是控制台端口(用于管理界面…...
全面解析数据库:从基础概念到前沿应用
在数字化时代,数据已成为企业和社会发展的核心资产,而数据库作为存储、管理和处理数据的关键工具,在各个领域发挥着举足轻重的作用。从电商平台的商品信息管理,到社交网络的用户数据存储,再到金融行业的交易记录处理&a…...
Pydantic + Function Calling的结合
1、Pydantic Pydantic 是一个 Python 库,用于数据验证和设置管理,通过 Python 类型注解强制执行数据类型。它广泛用于 API 开发(如 FastAPI)、配置管理和数据解析,核心功能包括: 数据验证:通过…...