【线程池】换个姿势来看线程池中不一样的阻塞队列(一)

news2024/11/22 4:26:35

前言

本文中无特别说明的话,线程池指的是

java.util.concurrent #ThreadPoolExecutor

本文只探讨线程池中阻塞队列相关,暂时不对线程池的其他方面进行说明,如果对线程池感兴趣的话,接下来几天我会多分享些和线程池相关的知识,和大家探讨下,比如:

  • 核心线程数及最大线程数如何根据业务场景进行合适的配置
  • 线程池中的异常是如何抛出的
  • 拒绝策略应该如何选择,有哪些坑
  • 如何解决使用线程池异步操作时的上下文传递        

阻塞队列在线程池中的常见使用

        ThreadPoolExecutor为了方便使用,提供了多个用来初始化的构造函数,我们来看看其中的一个常用的构造函数,下面第五个参数BlocklingQueue类型的workQueue就是线程池中的阻塞队列。

 

        提到线程池的时候,我们往往会想到池化技术、线程复用、线程管理等,而我理解其核心机制包括两个方面

  • 线程的管理

核心线程数、最大线程数的控制   

  • 任务的管理

任务什么时候需要存储、存储在哪里、怎么消费等

        阻塞队列是任务管理的核心实现,在多样的业务需求下,不同的阻塞队列,可以定制化的控制任务处理以及线程池中活跃线程的个数等,从而应对不同的业务场景需要。

        以下三个方面,阻塞队列不同的使用策略

  • 直接使用线程处理

        不使用队列对提交的任务进行缓存,

        例如使用SynchronousQueue 见名知意,此队列只提供了同步的功能

SynchronousQueue是一个没有数据缓冲的BlockingQueue。
 
一个线程的插入必须等待另一个线程的删除操作才能完成,反之亦然。
SynchronousQueue适合传递性设计(handoff designs),即一个线程中运行的对象,需要将某些信息、任务或者事件等传递给另一个线程中运行的对象的场景。
SynchronousQueue支持公平和非公平模式。
不能在同步队列上进行peek,因为仅在试图要移除元素时,该元素才存在。
不能迭代队列,因为其中没有元素可用于迭代。
Executors.newCachedThreadPool()中就使用了SynchronousQueue队列    
  • 无界队列

        最经典的LinkedBlockingQueue,基于内存可以无限大的队列。

        也是面试会问到阿里巴巴为什么不推荐使用Executors创建线程池,原因像如下线程池使用的阻塞队列是LinkedBlockingQueue,在高并发请求的情况下,无界队列会造成内存溢出(OOM)

Executors.newFixedThreadPool(5);
Executors.newSingleThreadExecutor();
  • 有界队列

        通过初始化阻塞队列的大小来控制可提交至线程池任务的个数,可根据业务场景的估算,设置队列的大小,如下图所示使用

ArrayBlockingQueue

        

你知道tomcat是怎么使用线程池的吗

        tomcat作为最常用的web服务器之一,其意义相对于HTTP请求来说,由于西方不能失去耶路撒冷,葫芦娃不能失去爷爷,野比大雄不能失去叮当猫,我们非常有必要来了解下,当tomcat接收到请求时,内部线程池是怎样使用的,阻塞队列起到了什么样的作用。

        当然了,你根本不知道tomcat有多努力,呸,你这个渣男。

        我们都知道对于JUC包下的ThreadPoolExecutor的任务提交时,任务执行机制流程是这样式的,贴一下来自美团技术团队的那篇文章(墙裂推荐大家去好好看一下,把玩其中的奥妙)的经典插图。

         关键点是在当阻塞队列已满时,线程池会继续添加工作线程直至到达最大线程数后,再次提交任务,会执行任务拒绝策略(这里拒绝策略的选择是有个大坑的,后续我再发文细说),

        当然了这里有个关键的前提,阻塞队列必须是个有界的队列。

        但是呢,请注意。

        tomcat在上述流程的处理采取了不一样的方式

  tomcat线程池的实现位于tomcat-embed-core-9.0.37.jar下,

org.apache.tomcat.util.threads # ThreadPoolExecutor

名称和JUC包下的线程池名称一模一样,

去github找到tomcat的ThreadPoolExecutor源码和JUC包下的ThreadPoolExecutor对比,几乎一模一样甚至注释也一样。

只不过tomcat针对自身的使用业务场景做了一点改动,从而更贴合自身的使用

       

        以下是tomcat线程池的入口处

 

        主要改动:tomcat线程池在线程大小到达核心线程数之后,并不直接入队列,而是先添加工作线程数之后再继续入队列,而这个队列是一个无界队列。

        tomcat这样做的好处是什么呢?

        先明确一下,tomcat是用来接收web的Http请求的,并发处理的请求数是要进行限制的,但是呢又不能限制业务请求进入系统,而上述流程确实满足了上述要求

  • 使用最大线程数来处理可能到来的瞬时流量,当请求变少时,闲置的线程也可以被回收
  • 使用无界队列来接收最大线程数也无法处理的任务,起到一个削峰添谷的作用。

        那么tomcat是如何实现的呢

        没错儿,就是使用自定义的阻塞队列TaskQueue实现的。

        关键点在于当活跃线程数达到核心线程数时,继续提交任务正常是会进队列,tomcat主体流程也是进队列,但是它搞了个骚操作换了个玩法,不得不让人竖起大拇哥,真是神奇他妈给神奇开门--神奇到家了。

        如下所示,TaskQueue是继承自LinkedBlockingQueue,但是它添加元素的offer()方法

中,当核心线程数小于最大线程数时,会返回false。

         offer()方法返回false之后,会继续创建线程addWorker()执行提交的任务,直至达到最大线程数之后,才会继续提交任务至无界队列中。

        

         tomcat中结合其应用场景,自定义阻塞队列达到了其预期的使用效果。

Dubbo中阻塞队列的使用姿势绝对是你未见过的船新版本

        不仅渣渣辉代言的传奇是船新版本,Dubbo中的阻塞队列我第一次见到时也是被惊艳到了,这个全新版本才是小刀剌(la)屁股---开了眼了。

        在GitHub冲浪时,在dubbo的issue里发现了这么个好东西,也帮大伙儿开开眼。

        这个issue要给dubbo中的线程池添加一个阻塞队列,MemorySafeLinkedBlockingQueue为了防止和下文中要讲解的另一个相似的队列混淆,我们简称这个队列为MMS 。[ISSUE #10020] add MemorySafeLinkedBlockingQueue by dragon-zhang · Pull Request #10021 · apache/dubbo · GitHub

看这个标题你是不是虎躯一震,看看这几个关键词,多么有冲击力

MemorySafe --内存安全

solve  OOM problem --解决内存溢出问题

easier than MemoryLimitedLinkedBlockingQueue --比限制内存的阻塞队列更容易

        在看这个issue提出的MemorySafeLinkedBlockingQueue之前,我们先来深入了解下上一个版本的MemoryLimitedLinkedBlockingQueue蕴含着怎么样的奥妙。

MemoryLimitedLinkedBlockingQueue ---MML

        以下我们简称MemoryLimitedLinkedBlockingQueue为  MML。

        现在在dubbo的issue中搜索一下这个阻塞队列是什么时候提交的,是解决了什么问题。找到了以下三个issue。

            可以看到MML目的是为了解决线程池中无界队列可能导致的OOM问题

 

   

        上图中ThreadPool就是dubbo中线程池自定义实现类,可以看到,提交者希望使用MML替换了原有的阻塞队列。

         来看下内部的实现

        核心逻辑是通过记录已使用内存可配置的限制内存的大小借助Instrumentation接口来获取当前要提交任务对象的大小,这三项来控制阻塞队列的占用内存大小,从而避免OOM的出现。

熟悉像javaagent、Arthas的童鞋,应该会对Instrumentation接口很熟悉,可以监控JVM的底层信息,进行插桩式开发

MemorySafeLinkedBlockingQueue ---MMS

        接下来我们来看下开始的时候提出的MMS是怎么防止OOM的

        其实MMS和上文聊的MML是反其道而行之,上文是控制最大使用内存,而MMS是控制JVM剩余内存即未使用内存的大小。

        MMS通过一个参数maxFreeMemory就实现了MML较为复杂的计算,更关键的是MMS并不需要关注添加至队列的每个任务对象的大小,也不需要累加已使用的内存,只需要关注剩余的内存就好,降低了复杂度,实现起来也非常优雅。

 核心类的作用是内部另起了一个定时线程来准实时的更新剩余内存的大小

        总体而言,MMS的实现更为简单轻量级,且用起来更方便。

        代码的世界总是那么的出人意料又合乎情理,很多时候不明白的代码原理,兜兜转转一圈之后,见识过更多精妙的设计,蓦然回首才发现原来真理已在身边久矣。

        正如本文介绍的线程池阻塞队列,在没见识到这些开源项目之前,我给它的用法贴了平平无奇的标签,我也没想到他们可以玩的这么花。

        一路走来,确实也见过不少风景,来让我描绘出来吧。

        【下集预告】---线程池中拒绝策略使用不当的巨坑

        都看到这里了,不如点个赞再走吧~~~

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

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

相关文章

基于蜻蜓算法优化的BP神经网络(预测应用) - 附代码

基于蜻蜓算法优化的BP神经网络(预测应用) - 附代码 文章目录 基于蜻蜓算法优化的BP神经网络(预测应用) - 附代码1.数据介绍2.蜻蜓优化BP神经网络2.1 BP神经网络参数设置2.2 蜻蜓算法应用 4.测试结果:5.Matlab代码 摘要…

软件测试报告:包含哪些内容?

软件测试报告的内容 软件测试报告通常包括以下内容: 1、项目背景:介绍测试报告的编写目的、测试系统名称、测试环境和用到的专业术语。 2、需求内容:罗列该项目的测试功能点,具体到每个模块功能,以新增的功能和修改的功能为主&…

jmeter HTTP请求默认值

首先,打开JMeter并创建一个新的测试计划。 右键单击测试计划,选择"添加" > “配置元件” > “HTTP请求默认值”。 在HTTP请求默认值中,您可以设置全局的HTTP请求属性,例如: 服务器地址&#xff1a…

cad图怎么转换成pdf格式?一招教你轻松转换

将CAD文件转换成PDF格式有很多优势。首先,PDF格式是一种非常流行的文件格式,几乎所有电脑上都可以打开。这意味着即使将PDF文件发送给其他人,他们也可以轻松地查看文件,此外,PDF格式可以保留CAD文件的图形和布局&#…

Rancher使用cert-manager安装报错解决

报错: rancher-rke-01:~/rke/rancher-helm/rancher # helm install rancher rancher-stable/rancher --namespace cattle-system --set hostnamewww.rancher.local Error: INSTALLATION FAILED: Internal error occurred: failed calling webhook "webhook…

Redis-设置密码linux服务器

操作步骤 打开Redis的配置文件,通常位于 /etc/redis/redis.conf。在配置文件中找到 #requirepass 或 requirepass 的行,如果存在的话,取消行首的注释符号 #。将密码设置为你想要的值,例如 requirepass YourPassword。确保将 Your…

嵌入式蓝海变红海?其实是大浪淘沙!

嵌入式是当下热门的职业方向之一,吸引了众多求职者的目光。然而,有人担心大家一拥而上,导致嵌入式就业竞争激烈,找工作难度大。其实,嵌入式行业的竞争并非无法逾越的天堑,也远远没有从蓝海变成红海&#xf…

RocketMQ MQTT使用教程

一、概览 传统的消息队列MQ主要应用于服务(端)之间的消息通信,比如电商领域的交易消息、支付消息、物流消息等等。然而在消息这个大类下,还有一个非常重要且常见的消息领域,即IoT类终端设备消息。近些年,我…

工采电子受邀参加:维科网举办的2023全球数字经济产业大会

先进电子科技将引领着绿色出行、绿色能源、绿色制造的未来发展,协助我们应对环境、经济及社会等种种机遇与挑战。让我们聚焦并探讨如何持续促进智能化、网联化、低碳化、电动化技术的融合发展,牵引和拉动并实现产业效益,相互赋能,…

算法与数据结构-二叉树

文章目录 什么是树什么是二叉树如何表示(或者存储)一棵二叉树链式存储法顺序存储法二叉树的分类 二叉树的遍历二叉查找树1. 二叉查找树的查找操作2. 二叉查找树的插入操作3. 二叉查找树的删除操作4.支持重复数据的二叉查找树 红黑树什么是“平衡二叉查找…

【SpringCloud】Sleuth链路追踪使用

文章目录 概述配置YMLPOM示例调用路径 概述 能够查看服务调用链路 官网: https://github.com/spring-cloud/spring-cloud-sleuth 在分布式系统中提供追踪解决方案并且兼容支持了zipkin 配置 SpringCloud从F版起已不需要自己构建Zipkin Server了,只需调…

成都大运会,保障大型活动无线电安全需要…

成都大运会 7月28日,备受关注的第31届世界大学生夏季运动会在成都正式开幕。据悉,这是全球首个5G加持的智慧大运会,也是众多成熟信息技术的综合“应用场”。使用基于5G三千兆、云网、8K超高清视频等技术,在比赛现场搭建多路8K摄像…

item_search_shop-获得淘宝/天猫店铺的所有商品

一、接口参数说明: item_search_shop-获得店铺的所有商品,点击更多API调试,请移步注册API账号点击获取测试key和secret 公共参数 请求地址: https://api-gw.onebound.cn/taobao/item_search_shop 名称类型必须描述keyString是调用key&…

怎么恢复qq聊天记录,还不会恢复的看过来!

QQ聊天记录丢失了该怎么办?QQ作为我们日常生活中常见的聊天软件,记录了我们与朋友、家人以及老师之间的重要对话。除此之外,QQ还是传输大型文件的好帮手,这也是为什么还有这么多人喜欢用QQ的原因。 但传输的文件一旦多起来&#…

利用Python下载王者荣耀的全部英雄高清海报【普通皮肤海报】

一、前言 前几天,有人让我帮他下载王者荣耀英雄海报,便想着利用Python写个程序,要不然一个个下载太麻烦,也不是咱的作风 二、库安装 pip install beautifulsoup4pip install lxml三、编辑代码 import requests, re from bs4 i…

yolov5 三个yolo层可视化

CAM方法 拉yolov5源码 在V5的模型中:第17-C3,20-C3,23-C3是三个yolo层的输出,需要明确一下哪一层的输出捕获到了目标? C3的前向传播:C3的 conv3是C3模块的最后输出;通过看C3和bottleNeck的forward代码可以看出&…

Linux系统下的文件目录结构

一、单用户操作系统和多用户操作系统 单用户操作系统:指一台计算机在同一时间内只能由一个用户使用,一个用户独自享用系统的全部硬件和软件资源 Windows XP之前的版本都是单用户操作系统 多用户操作系统:指一台计算机在同一时间可以由多个用户使用&…

【C++STL基础入门】深入理解string类重新赋值(assign)与删除(erase)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、重新赋值1.重新赋值指定元素2.assign函数 二、删除指定元素1.erase函数2.删除全部使用erase()使用clear() 总结 前言 本系列STL使用的是VS2022,…

龙讯旷腾PWmat已部署至曙光智算平台

编者荐语: 近期,龙讯旷腾核心产品PWmat已成功部署至曙光智算AC.sugon.com平台,可为用户提供包括分子建模、第一性原理计算、数据可视化等在内的完备的超级计算云服务,让大家能够轻松上手具有完全自主知识产权的大尺度高性能材料计…

公用医学数据库有哪些?

公共卫生领域的科学研究和知识获取正日益依赖于医学数据库。本文整理了一些公用医学数据库,希望对你有帮助。先收藏,再继续看。 一、中国营养健康调查CHNS数据库 https://www.cpc.unc.edu/projects/china 中国健康与营养调查(China Heal…