当前位置: 首页 > news >正文

源码分析CompletableFuture使用默认线程池ForkJoinPool的弊端

先说结论:
假如有20CompletableFuture任务并发执行时,都使用默认线程池ForkJoinPool,但cpu的核心数又小于3,那么就会新建20个线程(不使用默认线程池了),这20个线程相互竞争cpu资源和内存,很多线程都在等待,浪费了大量的性能在线程上下文切换上。

线程池大小设定:

  • 如果服务是cpu密集型的,设置为电脑的核数
  • 如果服务是io密集型的,设置为电脑的核数*2

runAsync方法点进去

    CompletableFuture<Void> voidCompletableFuture = CompletableFuture.runAsync(() -> {System.out.println(1);});

可以看见使用的是asyncPool。点进asyncPool

    public static CompletableFuture<Void> runAsync(Runnable runnable) {return asyncRunStage(asyncPool, runnable);}

useCommonPool是否为true决定了使用 ForkJoinPool线程池还是新建一个线程池。点进useCommonPool。

    private static final Executor asyncPool = useCommonPool ?ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();

这里判定的是ForkJoinPool common线程池中并行度级别是否大于1。点进 getCommonPoolParallelism() 方法

    private static final boolean useCommonPool =(ForkJoinPool.getCommonPoolParallelism() > 1);

返回的是commonParallelism这个字段,再往下找。

    public static int getCommonPoolParallelism() {return commonParallelism;}

发现只有一个地方对这个属性进行赋值,继续。

    static final int commonParallelism;

在这里插入图片描述

发现commonParallelism 由par决定,par来自common.config SMASK做与运算。SMASK定义为0xffff(65535),common.config由 makeCommonPool()得到。点进makeCommonPool()方法

...
static final ForkJoinPool common;
static {...common = java.security.AccessController.doPrivileged(new java.security.PrivilegedAction<ForkJoinPool>() {public ForkJoinPool run() {return makeCommonPool(); //common的config由此方法返回}});int par = common.config & SMASK; // report 1 even if threads disabledcommonParallelism = par > 0 ? par : 1;
}

在这里插入图片描述

我简化了下面源码,parallelism 初始化为 -1,若jvm启动参数有java.util.concurrent.ForkJoinPool.common那么parallelism将会被启动参数指定。Runtime.getRuntime().availableProcessors() 是获取虚拟机可使用的处理器数量。在jvm未定义参数的前提下,处理器数量若小于等于2,那么并行度parallelism就为1,反之则为 (处理器数量 - 1)。定义了jvm参数,若参数值大于MAX_CAP(32767),则重新赋值。即parallelism 总会大于0, 继续点进ForkJoinPool的构造方法。

    private static ForkJoinPool makeCommonPool() {...int parallelism = -1;try {  // ignore exceptions in accessing/parsing propertiesString pp = System.getProperty("java.util.concurrent.ForkJoinPool.common.parallelism");if (pp != null)parallelism = Integer.parseInt(pp);} catch (Exception ignore) {}if (parallelism < 0 && // default 1 less than #cores(parallelism = Runtime.getRuntime().availableProcessors() - 1) <= 0)parallelism = 1;if (parallelism > MAX_CAP)parallelism = MAX_CAP;return new ForkJoinPool(parallelism, factory, handler, LIFO_QUEUE, // 注意 LIFO_QUEUE等于0 "ForkJoinPool.commonPool-worker-");}

发现config也是作位运算,即config也会大于0,我们拿到这个config返回开始 par 赋值那一块。

    private ForkJoinPool(int parallelism,ForkJoinWorkerThreadFactory factory,UncaughtExceptionHandler handler,int mode,String workerNamePrefix) {this.workerNamePrefix = workerNamePrefix;this.factory = factory;this.ueh = handler;this.config = (parallelism & SMASK) | mode; // SMASK等于65535,mode等于0long np = (long)(-parallelism); // offset ctl countsthis.ctl = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK);}

总结就是并行度 parallelism(大于0) 与 65535(111111…) 做两次与运算,即本身。继续回到之前

...
static final ForkJoinPool common;
static {...common = java.security.AccessController.doPrivileged(new java.security.PrivilegedAction<ForkJoinPool>() {public ForkJoinPool run() {return makeCommonPool(); //common的config由此方法返回}});int par = common.config & SMASK; // report 1 even if threads disabledcommonParallelism = par > 0 ? par : 1;
}

这里判断,

  • 无JVM参数前提下:
    • 若服务器的核心数小于等于2,commonParallelism 则为1,即useCommonPool 为false,new 一个线程池。
    • 若服务器的核心数大于2,commonParallelism 则为 核心数 - 1,即useCommonPool 为true,使用ForkJoinPool线程池。
  • 有JVM参数,以设置参数为准。大于1小于等于32767。和上面判断一致。
    private static final Executor asyncPool = useCommonPool ?ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();private static final boolean useCommonPool =(ForkJoinPool.getCommonPoolParallelism() > 1);public static int getCommonPoolParallelism() {return commonParallelism;}

在ThreadPerTaskExecutor 中 execute,他会为每个任务新开一个线程,而不是采用ForkJoinPool中的线程。

    static final class ThreadPerTaskExecutor implements Executor {public void execute(Runnable r) { new Thread(r).start(); }}

结论:jvm启动参数中 java.util.concurrent.ForkJoinPool.common 的值为 1或者服务器核心数小于等于2,都会导致不采用ForkJoinPool 中的线程,而是新起一个线程。

如果服务器只有两核,假如业务需要:现在起了20个completablefuture任务,若使用默认线程池,那么就会创建20个线程,20个线程并发执行在两个cpu上竞争稀缺的处理器和内存资源,浪费大量的时间在上下文切换上。

相关文章:

源码分析CompletableFuture使用默认线程池ForkJoinPool的弊端

先说结论&#xff1a; 假如有20CompletableFuture任务并发执行时&#xff0c;都使用默认线程池ForkJoinPool&#xff0c;但cpu的核心数又小于3&#xff0c;那么就会新建20个线程&#xff08;不使用默认线程池了&#xff09;&#xff0c;这20个线程相互竞争cpu资源和内存&#x…...

连接pgsql数据库 sslmode sslrootcert sslkey sslcert 参数的作用

sslmode 参数的作用 sslmode 参数用于指定数据库连接时使用的 SSL 加密模式。SSL&#xff08;Secure Sockets Layer&#xff09;是一种加密协议&#xff0c;用于保护数据在客户端和服务器之间的传输过程&#xff0c;以增加数据传输的安全性。sslmode 参数可以设置不同的值&…...

从零学算法3

3.给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串 的长度。 示例 1: 输入: “abcabcbb” 输出: 3 解释: 因为无重复字符的最长子串是 “abc”&#xff0c;所以其长度为 3。 示例 2: 输入: “bbbbb” 输出: 1 解释: 因为无重复字符的最长子串是 “b”&…...

宠物小程序开发

在当今社会&#xff0c;宠物已成为许多人生活中不可或缺的一部分。宠物市场的持续增长为创业者提供了巨大的商机。然而&#xff0c;作为一个创业者&#xff0c;要在竞争激烈的宠物市场中脱颖而出并不容易。因此&#xff0c;开发一个专属于自己的宠物小程序成为了解决这一难题的…...

07-Vue基础之综合案例——小黑记事本

个人名片&#xff1a; &#x1f60a;作者简介&#xff1a;一名大二在校生 &#x1f921; 个人主页&#xff1a;坠入暮云间x &#x1f43c;座右铭&#xff1a;懒惰受到的惩罚不仅仅是自己的失败&#xff0c;还有别人的成功。 &#x1f385;**学习目标: 坚持每一次的学习打卡 文章…...

vite4+vue3+electron23.3+ts桌面应用bs端开发 打包windows、linux、max三个系统的安装包

vite4vue3electron23.3ts桌面应用bs端开发 打包windows、linux、max三个系统的安装包 主要包依赖 "electron-store": "^8.1.0", //全局数据状态管理&#xff0c;可选择性安装"electron": "23.3.8","electron-builder": &q…...

限制 el-input 输入 emoji

1. 电脑如何输入 emoji 表情 ? 快捷键 win; 或 win. 2. 代码实现 <template><el-input v-model"input" placeholder"请输入内容" input"inputChange"></el-input> </template><script> export default {name: D…...

【AI】解决Number_Words的安装和使用

It appears that you encountered an error while trying to install the “Numbers_Words” package using the specific version 0.18.2 of the PEAR channel. The error message indicates that there was a problem unpacking the “Math_BigInteger-1.0.3” package, whi…...

开启MySQL的binlog日志

在/etc/my.cnf增加如下配置 #binlog相关 log-bin /testdata/mysql/log/bin/mysql-bin expire_logs_days 7 binlog-format ROW binlog_cache_size 4M max_binlog_cache_size 20G binlog_rows_query_log_events 1 binlog_row_image FULL sync_binlog 1 log_bin_trust_fun…...

【支付宝小程序】支付宝小程序自定义组件技术教程

&#x1f996;我是Sam9029&#xff0c;一个前端 Sam9029的CSDN博客主页:Sam9029的博客_CSDN博客-JS学习,CSS学习,Vue-2领域博主 **&#x1f431;‍&#x1f409;&#x1f431;‍&#x1f409;恭喜你&#xff0c;若此文你认为写的不错&#xff0c;不要吝啬你的赞扬&#xff0c…...

CSDN编程题-每日一练(2023-08-23)

CSDN编程题-每日一练(2023-08-23) 一、题目名称:圆小艺二、题目名称:连续子数组的最大和三、题目名称:投篮一、题目名称:圆小艺 时间限制:1000ms内存限制:256M 题目描述: 最近小艺酱渐渐变成了一个圆滑的形状-球!! 小艺酱开始变得喜欢上球! 小艺酱得到n个同心圆。 …...

解决:Appium Inspector刷新页面一直加载转圈

目录 问题&#xff1a;Appium Inspector刷新页面一直加载转圈 解决办法&#xff1a; 1.进入设置页面-电池-后台耗电管理 2.找到下面3个应用&#xff0c;修改为允许后台高耗电 问题&#xff1a;Appium Inspector刷新页面一直加载转圈 1、手机进行操作后&#xff0c;Appium I…...

Apache Doris 入门教程34:Join 优化

Bucket Shuffle Join Bucket Shuffle Join 是在 Doris 0.14 版本中正式加入的新功能。旨在为某些 Join 查询提供本地性优化&#xff0c;来减少数据在节点间的传输耗时&#xff0c;来加速查询。 它的设计、实现和效果可以参阅 上面的图片展示了Bucket Shuffle Join的工作原理…...

【神州数码】BGP路由器案例

SwitchB、SwitchC和SwitchD位于AS200中&#xff0c;SwitchA位于AS100中。SwitchA和SwitchB共享一个相同的网络段11.0.0.0。而SwitchB和SwitchD彼此物理上不相邻。 则SwitchA的配置如下&#xff1a; SwitchA(config)#router bgp 100SwitchA(config-router-bgp)#neighbor 11.1.1…...

gin框架实现大文件下载

在gin框架中实现大文件下载主要分为两个步骤&#xff1a; 将文件分块读取 由于大文件一次性读取会占用大量内存&#xff0c;容易导致内存溢出等问题&#xff0c;需要将文件分块读取&#xff0c;逐一发送给客户端。 在gin框架中&#xff0c;可以使用context.File方法向客户端…...

数据可视化-canvas-svg-Echarts

数据可视化 技术栈 canvas <canvas width"300" height"300"></canvas>当没有设置宽度和高度的时候&#xff0c;canvas 会初始化宽度为 300 像素和高度为 150 像素。切记不能通过样式去设置画布的宽度与高度宽高必须通过属性设置&#xff0c;…...

深信服 SG上网优化管理系统 catjs.php 任意文件读取漏洞[2023-HW]

深信服 SG上网优化管理系统 catjs.php 任意文件读取漏洞 一、漏洞描述二、漏洞影响三、网络测绘四、漏洞复现小龙POC检测: 五、 修复建议 免责声明&#xff1a;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间…...

java反序列化泛型中json对象

使用 jackson的objectMapper 来实现 import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMa…...

Docker Compose一键管理容器

可以一键批量管理docker的容器。将所有需要创建的容器定义在compose配置文件中&#xff0c;通过一个命令一键可以创建并运行这些容器&#xff0c;而不需要一个一个启动。可以批量启动停止服务。 安装 #安装Docker-Compose并安装到/usr/local/bin/docker-compose curl -L &quo…...

API接口文档利器:Swagger 和 接口调试利器:Postman

2.接口相关工具 2.1API接口文档利器&#xff1a;Swagger 2.1.1Swagger介绍 Swagger 是一个规范和完整的框架&#xff0c;用于生成、描述、调用和可视化 RESTful 风格的 Web 服务 (https://swagger.io/)。 它的主要作用是&#xff1a; 使得前后端分离开发更加方便&#xff0…...

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…...

mongodb源码分析session执行handleRequest命令find过程

mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程&#xff0c;并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令&#xff0c;把数据流转换成Message&#xff0c;状态转变流程是&#xff1a;State::Created 》 St…...

FastAPI 教程:从入门到实践

FastAPI 是一个现代、快速&#xff08;高性能&#xff09;的 Web 框架&#xff0c;用于构建 API&#xff0c;支持 Python 3.6。它基于标准 Python 类型提示&#xff0c;易于学习且功能强大。以下是一个完整的 FastAPI 入门教程&#xff0c;涵盖从环境搭建到创建并运行一个简单的…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止

<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet&#xff1a; https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...

第25节 Node.js 断言测试

Node.js的assert模块主要用于编写程序的单元测试时使用&#xff0c;通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试&#xff0c;通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

生成 Git SSH 证书

&#x1f511; 1. ​​生成 SSH 密钥对​​ 在终端&#xff08;Windows 使用 Git Bash&#xff0c;Mac/Linux 使用 Terminal&#xff09;执行命令&#xff1a; ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" ​​参数说明​​&#xff1a; -t rsa&#x…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现

摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序&#xff0c;以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务&#xff0c;提供稳定高效的数据处理与业务逻辑支持&#xff1b;利用 uniapp 实现跨平台前…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍

文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结&#xff1a; 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析&#xff1a; 实际业务去理解体会统一注…...

.Net Framework 4/C# 关键字(非常用,持续更新...)

一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...

Java毕业设计:WML信息查询与后端信息发布系统开发

JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发&#xff0c;实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构&#xff0c;服务器端使用Java Servlet处理请求&#xff0c;数据库采用MySQL存储信息&#xff0…...