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对象(notFullnotEmpty)来管理生产和消费的过程。

为什么在某些情况下ReentrantLock的表现优于synchronized?

ReentrantLock提供了比synchronized更灵活的功能,例如可以尝试获取锁、支持公平锁、允许锁中断等特性。此外,在高并发场景下,ReentrantLock的性能可能优于synchronized,因为它避免了线程进入内核态的阻塞状态。不过需要注意的是,在低并发的情况下,synchronized的性能表现可能会更好。

设计一个场景,说明何时应该选择使用读写锁而不是普通的互斥锁

假设有一个缓存系统,其中读取操作远远多于写入操作。在这种情况下,如果使用普通的互斥锁,则每次读取时都会阻止其他读取操作的发生,即使它们不会相互影响。而使用读写锁(如ReentrantReadWriteLock),则可以在没有写入操作发生时允许多个读取操作同时进行,从而提高了系统的并发度和响应速度。

实现一个简单的银行账户类

对于银行账户类,我们可以分别使用synchronizedReentrantLock来保证线程安全。以下是两种实现方式:

使用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是一个实现了RunnableFuture接口的具体类,它可以包装一个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的具体实现细节。

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

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

相关文章

以太网链路详情

文章目录 1、交换机1、常见的概念1、冲突域2、广播域3、以太网卡1、以太网卡帧 4、mac地址1、mac地址表示2、mac地址分类3、mac地址转换为二进制 2、交换机的工作原理1、mac地址表2、交换机三种数据帧处理行为3、为什么会泛洪4、转发5、丢弃 3、mac表怎么获得4、同网段数据通信…

IDEA 2024 版本配置热部署

在实际开发过程中&#xff0c;每次修改代码就得将项目重启&#xff0c;重新部署&#xff0c;对于一些大型应用来说&#xff0c;重启时间需要花费大量的时间成本。对于一个后端开发者来说&#xff0c;重启过程确实很难受啊 采用下面三步骤可以完成 1.在IDEA中settings中搜索Debb…

阿里云 云产品流转(实现设备与小程序交互)

一、准备工作 1、设备接入平台 创建两个设备接入到对应产品中&#xff0c;具体可以参考这里&#xff08;点击跳转&#xff09;。 二、云产品流转设置 在物联网平台下-->消息转发-->云产品流转->数据源 1、数据源 数据源-->创建数据源-->填写信息-->确定&…

单目深度估计模型 lite-mono 测试

lite-mono 使用工业数据集kitti 进行训练&#xff0c;目的使用单目摄像头实现物体深度预测&#xff0c;关于kitti数据集的介绍和下载参考 &#xff08;二&#xff09;一文带你了解KITTI数据集-CSDN博客文章浏览阅读2.7w次&#xff0c;点赞64次&#xff0c;收藏294次。文章介绍…

.Net 多线程、异步、性能优化应用与心得

文章目录 概要多线程Thread方式创建线程:Task方式创建线程[C#5.0引入]&#xff08;推荐使用&#xff09;:线程池方式创建线程&#xff1a; 异步异步方法异步IO操作异步数据库操作异步Web请求取消异步ValueTask[C# 7.0引入]ValueTask<TResult> 和 Task 性能优化懒加载对象…

qt-C++语法笔记之mapToGlobal将组件(控件)中的本地坐标系(局部坐标)映射到全局坐标系

qt-C语法笔记之mapToGlobal将组件&#xff08;控件&#xff09;中的本地坐标系&#xff08;局部坐标&#xff09;映射到全局坐标系 code review! 文章目录 qt-C语法笔记之mapToGlobal将组件&#xff08;控件&#xff09;中的本地坐标系&#xff08;局部坐标&#xff09;映射到…

python爬虫--小白篇【爬取B站视频】

目录 一、任务分析 二、网页分析 三、任务实现 一、任务分析 将B站视频爬取并保存到本地&#xff0c;经过分析可知可以分为四个步骤&#xff0c;分别是&#xff1a; 爬取视频页的网页源代码&#xff1b;提取视频和音频的播放地址&#xff1b;下载并保存视频和音频&#x…

UniScene:Video、LiDAR 和Occupancy全面SOTA

论文: https://arxiv.org/pdf/2412.05435 项目页面&#xff1a;https://arlo0o.github.io/uniscene/ 0. 摘要 生成高保真度、可控制且带有标注的训练数据对于自动驾驶至关重要。现有方法通常直接从粗糙的场景布局生成单一形式的数据&#xff0c;这不仅无法输出多样化下游任务…

Ubuntu22.04搭建FTP服务器保姆级教程

在网络环境中&#xff0c;文件传输是一项至关重要的任务。FTP&#xff08;文件传输协议&#xff09;是一种基于客户端/服务器模式的协议&#xff0c;广泛用于在互联网上传输文件。Ubuntu作为一款流行的Linux发行版&#xff0c;因其稳定性和易用性而广受开发者和系统管理员的喜爱…

【银河麒麟高级服务器操作系统】修改容器中journal服务日志存储位置无效—分析及解决方案

了解更多银河麒麟操作系统全新产品&#xff0c;请点击访问 麒麟软件产品专区&#xff1a;https://product.kylinos.cn 开发者专区&#xff1a;https://developer.kylinos.cn 文档中心&#xff1a;https://documentkylinos.cn 服务器环境以及配置 【机型】 整机类型/架构&am…

React 第十六节 useCallback 使用详解注意事项

useCallback 概述 1、useCallback 是在React 中多次渲染缓存函数的 Hook&#xff0c;返回一个函数的 memoized的值&#xff1b; 2、如果多次传入的依赖项不变&#xff0c;那么多次定义的时候&#xff0c;返回的值是相同的,防止频繁触发更新&#xff1b; 3、多应用在 父组件为函…

二十七、Tomcat专题总结与拓展

文章目录 一、Tomcat设计思路总结1、Tomcat整体架构2、Tomcat设计思路 二、Tomcat源码设计精髓三、拓展&#xff1a;SpringBoot整合Tomcat源码分析四、拓展&#xff1a;SpringBoot整合Undertow实战1、Undertow概述2、SpringBoot集成Undertow2.1、引入依赖2.2、application.prop…

[游戏开发] Unity中使用FlatBuffer

什么是FlatBuffer 官网&#xff1a; GitHub - google/flatbuffers: FlatBuffers: Memory Efficient Serialization LibraryFlatBuffers: Memory Efficient Serialization Library - google/flatbuffershttps://github.com/google/flatbuffers 为什么用FloatBuffer&#xff0c…

【JAVA】旅游行业中大数据的使用

一、应用场景 数据采集与整合&#xff1a;全面收集旅游数据&#xff0c;如客流量、游客满意度等&#xff0c;整合形成统一数据集&#xff0c;为后续分析提供便利。 舆情监测与分析&#xff1a;实时监测旅游目的地的舆情信息&#xff0c;运用NLP算法进行智能处理&#xff0c;及…

android studio创建虚拟机注意事项

emulator 启动模拟器的时候&#xff0c;可以用 AVD 界面&#xff0c;也可以用命令行启动&#xff0c;但命令行启 动的时候要注意&#xff0c;系统有两个 emulator.exe &#xff0c;建议使用 emulator 目录下的那个&#xff01;&#xff01; 创建类型为google APIs的虚拟机可从…

全面解析租赁小程序的功能与优势

内容概要 租赁小程序正在逐渐改变人与物之间的互动方式。通过这些小程序&#xff0c;用户不仅可以轻松找到所需的租赁商品&#xff0c;还能够享受无缝的操作体验。为了给大家一个清晰的了解&#xff0c;下面我们将重点介绍几个核心功能。 建议&#xff1a;在选择租赁小程序时&…

JCR一区牛顿-拉夫逊优化算法+分解对比!VMD-NRBO-Transformer-BiLSTM多变量时序光伏功率预测

JCR一区牛顿-拉夫逊优化算法分解对比&#xff01;VMD-NRBO-Transformer-BiLSTM多变量时序光伏功率预测 目录 JCR一区牛顿-拉夫逊优化算法分解对比&#xff01;VMD-NRBO-Transformer-BiLSTM多变量时序光伏功率预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.中科院…

用二维图像渲染3D场景视频

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

每日一练:链表-重排链表

LCR 026. 重排链表 - 力扣&#xff08;LeetCode&#xff09; 题目要求&#xff1a; 给定一个单链表 L 的头节点 head &#xff0c;单链表 L 表示为&#xff1a; L0 → L1 → … → Ln-1 → Ln 请将其重新排列后变为&#xff1a; L0 → Ln → L1 → Ln-1 → L2 → Ln-2 → ……

国产物联网平台(IotSharp+IoTGateway+Influxdb)快速上手

环境说明&#xff1a; Visual Studio 2022 CommunityIotSharp代码&#xff1a;https://github.com/IoTSharp/IoTSharp.gitIoTGateway版本&#xff1a;v2.1.1Node版本&#xff1a;v20.18.1Influxdb版本&#xff1a;v2.7.11 安装Node Node.js官网 官网下载并安装&#xff0c;…