深入理解ForkJoin
任务类型
线程池执行的任务可以分为两种:CPU密集型任务和IO密集型任务。在实际的业务场景中,我们需要根据任务的类型来选择对应的策略,最终达到充分并合理地使用CPU和内存等资源,最大限度地提高程序性能的目的。
CPU密集型任务
CPU密集型任务也称为计算密集型任务,包括加密、解密、压缩和计算等一系列需要大量耗费CPU资源的任务。对于CPU密集型的任务,并不是设置越多的线程性能越高,其最佳核心数是逻辑CPU核心数的1~2倍。因为对于计算任务较重的任务,CPU的每个核心基本都是满负荷的,设置更多的线程不仅不会提升性能,反而由于线程之间对CPU资源的争抢造成不必要的上下文切换导致性能下降。
IO密集型任务
IO密集型任务的特点是不会特别消耗CPU资源,但IO操作比较耗时,导致总体占用较多时间,数据库读写、文件读写、网络通信等任务都属于IO密集型任务。对于IO密集型任务,一般会将最大线程数设置为CPU核心数很多倍。IO读写速度相比于CPU计算的速度要慢很多,如果设置的线程数较少,线程可能都在处理IO操作,导致CPU资源的浪费。如果设置较多的线程数,当一部分线程在等待IO的时候,这部分线程不需要CPU资源,其他的线程就可以利用CPU资源去执行其他任务。
线程数计算方法
可以通过下面的公式计算出一个合理的线程数量。
线程数 = CPU核心数 × (1 + 任务平均等待时间/CPU平均工作时间)
可以看出线程数与任务平均等待时间成正比,任务的平均等待时间越长,线程数就越多;与CPU平均工作时间成反比,CPU平均工作时间越长,线程数就越少。例如CPU密集型任务的特点是CPU平均工作时间较长,而任务的平均等待时间较短,因此此类型所需的线程数较少;IO密集型任务的特点是任务平均等待时间较长,CPU平均工作时间较短,因此此类型所需的线程数较多。
分治算法
分治算法的基本思想是将一个规模较大的问题分解为多个规模较小的子问题,这些子问题之间相互独立且与原问题的性质相同。将所有子问题的解求出来,原问题的解也就求出来了。
分治算法的步骤如下:
- 分解:将要解决的问题划分成若干规模较小的同类子问题;
- 求解:当子问题划分得足够小时,可以用很简单的方式计算出子问题的解;
- 合并:将子问题的解逐层合并,最终得到原问题的解。
以上过程可以表示如下图:
ForkJoin框架
传统的线程池ThreadPoolExecutor有以下两个缺点:
- 无法对大任务进行拆分,即只能由单个线程去完成某项任务;
- 工作线程从队列中获取任务时存在竞争。
为了解决以上问题,JDK1.7引入了ForkJoin框架。ForkJoin框架允许其他线程向其提交任务,并将任务拆分成粒度更细的子任务,这些子任务由ForkJoin框架内部的工作线程来并行执行,并且这些工作线程之间可以互相窃取任务执行。
主要API
ForkJoin框架主要包含两部分:
- 分治任务的线程池ForkJoinPool类;
- 分治任务ForkJoinTask类。
ForkJoinPool
ForkJoinPool是用于执行ForkJoinTask任务的执行池,继承了AbstractExecutorService类。ForkJoinPool的构造函数有多个,此处我们介绍其中参数最全的一个,其实现如下:
public ForkJoinPool(int parallelism,ForkJoinWorkerThreadFactory factory,UncaughtExceptionHandler handler,boolean asyncMode) {this(checkParallelism(parallelism),checkFactory(factory),handler,asyncMode ? FIFO_QUEUE : LIFO_QUEUE,"ForkJoinPool-" + nextPoolId() + "-worker-");checkPermission();
}
- parallelism:表示指令的并行级别,ForkJoinPool将根据这个值来决定工作线程的数量,默认使用Runtime.getRuntime().availableProcessors()来设置;
- factory:ForkJoinPool内部创建线程使用的线程工厂,需要注意的是,此处的线程工厂的类型是ForkJoinWorkerThreadFactory而不是ThreadFactory,默认使用DefaultForkJoinWorkerThreadFactory;
- handler:异常处理器,主要用于处理任务运行中出现的异常;
- asyncMode:队列的工作模式,为true时使用先进先出模式,为false时使用先进后出模式。
ForkJoinPool提交任务的方法主要有以下几种:
- execute():在提交任务后不会返回结果,支持ForkJoinTask类型和Runnable两种类型的任务;
public void execute(ForkJoinTask<?> task);
public void execute(Runnable task);
- invoke():在任务执行结束后返回泛型执行结果,支持ForkJoinTask类型的任务;
public <T> T invoke(ForkJoinTask<T> task);
- submit():在提交任务后返回ForkJoinTask类型的结果,如果任务不能按计划执行则抛出任务拒绝异常,支持ForkJoinTask类型、Callable类型和Runnable类型的任务。
public <T> ForkJoinTask<T> submit(Callable<T> task);
public <T> ForkJoinTask<T> submit(Runnable task, T result);
public ForkJoinTask<?> submit(Runnable task);
ForkJoinTask
ForkJoinTask是ForkJoinPoll的核心之一,是任务的实际载体,定义了任务执行时的具体逻辑和任务拆分逻辑。ForkJoinTask是一个抽象类,实现了Future接口,因此也可以将其看成轻量级的Future。
ForkJoinTask的核心方法主要有以下两个:
- fork():用于向当前任务所运行的线程池中提交任务,如果当前线程是ForkJoinWorkThread类型(即ForkJoinPool内部工作线程),则将任务放入该线程的工作队列,否则放入common线程池的工作队列;
- join():用于获取任务的执行结果,调用此方法时将阻塞当前线程直到对应的子任务完成运行并返回结果。
ForkJoinTask提供了以下三个子类,在实际使用时可以根据需要继承这三个子类即可。
- RecursiveAction:用于递归执行但不需要返回结果的任务;
- RecursiveTask:用于递归执行且需要返回结果的任务;
- CountedCompleter<T>:在任务完成后会触发执行一个自定义的钩子方法。
WorkQueue
WorkQueue是ForkJoinPool的静态内部类,主要用来表示一个工作队列,是基于一个ForkJoinTask类型的数组实现的双端队列。WorkQueue的几个较重要的成员变量如下:
//在对该WorkQueue操作时搭配CAS加锁
volatile int qlock;
//数组的底端,工作窃取时从此处取出任务执行
volatile int base;
//任务数组,用于保存当前工作队列的所有任务
ForkJoinTask<?>[] array;
//当前工作队列的ForkJoinWorkerThread类型的工作线程,如果是外部提交生成的工作队列这个属性为null
final ForkJoinWorkerThread owner;
ForkJoinPool内部维护了一个WorkQueue工作队列数组,当我们调用invoke()或submit()方法提交一个任务时,ForkJoinPool根据某个路由规则将该任务放到一个工作队列中,如果任务在执行过程中会创建出子任务,则子任务会提交到工作线程对应的工作队列中。
工作队列WorkQueue是ForkJoinPool类的一个内部类,是一个双端队列。如果是ForkJoinPool内部划分的任务,在WorkQueue数组中的下标是奇数位;如果是外部提交的任务,在WorkQueue数组中的下标是偶数位。
每个工作线程在处理自己的工作队列时采用的是FILO先进后出的方式,即在运行中产生新的任务时,会将其放到工作队列的尾端;在需要执行新的任务时,同样在工作队列的尾端取任务。工作线程处理自己的工作队列的同时,还可以窃取其他工作线程的任务,为了降低冲突的发生,窃取的位置在工作队列的首端。
ForkJoinWorkerThread
ForkJoinWorkerThread是ForkJoinPool中用于执行任务的线程,并且是ForkJoinPool内部专门为执行划分的子任务创建的工作线程。每个ForkJoinWorkerThread都有其对应的一个WorkQueue,这个关系由WorkQueue的一个owner属性来维护,并且这种WorkQueue在ForkJoinPool的WorkQueue数组中的下标一定是奇数位。
工作窃取
工作窃取指的是允许空闲线程从繁忙线程的工作队列中窃取任务。一般情况下,工作线程是从它自己对应的工作队列(WorkQueue数组)的头部获取任务执行,但当它的工作队列为空时会从其他繁忙的工作线程的工作队列的尾部窃取任务来执行。工作窃取是ForkJoinPool的性能保证的关键之一。
工作窃取算法的优点是充分利用线程资源来执行任务,但在某些情况下工作窃取的设计会发生线程竞争问题,例如当双端队列只有一个任务。
工作窃取时会从其他工作线程的尾部窃取任务,主要有以下原因:
- 尽量降低线程竞争的可能,因为工作线程从自己的工作队列获取任务是在头部,工作窃取在尾部;
- 由于任务是可分割的,队列中较旧的任务的粒度可能相对较大,空闲的工作线程更适合处理这些任务。
ForkJoinPool使用
ForkJoin框架是基于分治算法实现的,并且其主要特点是可以实现任务切分,因此它的使用也与其他基于分治算法的实现类似。
ForkJoinPool执行流程
相关文章:

深入理解ForkJoin
任务类型 线程池执行的任务可以分为两种:CPU密集型任务和IO密集型任务。在实际的业务场景中,我们需要根据任务的类型来选择对应的策略,最终达到充分并合理地使用CPU和内存等资源,最大限度地提高程序性能的目的。 CPU密集型任务 …...

Spring5学习笔记—AOP编程
✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉 🍎个人主页:Leo的博客 💞当前专栏: Spring专栏 ✨特色专栏: M…...
适用于 Docker 用户的 kubectl
适用于 Docker 用户的 kubectl 你可以使用 Kubernetes 命令行工具 kubectl 与 API 服务器进行交互。如果你熟悉 Docker 命令行工具, 则使用 kubectl 非常简单。但是,Docker 命令和 kubectl 命令之间有一些区别。以下显示了 Docker 子命令, 并…...

网络安全设备篇——加密机
加密机是一种专门用于数据加密和解密的网络安全设备。它通过使用密码学算法对数据进行加密,从而保护数据的机密性和完整性。加密机通常被用于保护敏感数据,如金融信息、个人身份信息等。 加密机的主要功能包括: 数据加密:加密机使…...
Rust 基础入门 —— 2.3.所有权和借用
Rust 的最主要光芒: 内存安全 。 实现方式: 所有权系统。 写在前面的序言 因为我们这里实际讲述的内容是关于 内存安全的,所以我们最好先复习一下内存的知识。 然后我们,需要理解的就只有所有权概念,以及为了开发便…...
Node.js-Express框架基本使用
Express介绍 Express是基于 node.js 的web应用开发框架,是一个封装好的工具包,便于开发web应用(HTTP服务) Express基本使用 // 1.安装 npm i express // 2.导入 express 模块 const express require("express"); // 3…...

阿里云通用算力型u1云服务器CPU性能详细说明
阿里云服务器u1是通用算力型云服务器,CPU采用2.5 GHz主频的Intel(R) Xeon(R) Platinum处理器,通用算力型u1云服务器不适用于游戏和高频交易等需要极致性能的应用场景及对业务性能一致性有强诉求的应用场景(比如业务HA场景主备机需要性能一致)ÿ…...

设计模式之创建者模式
文章目录 一、介绍二、应用三、案例1. 麦当劳11随心配2. 代码演示3. 演示结果 四、优缺点五、送给读者 一、介绍 建造者模式(Builder Pattern)属于创建型设计模式,很多博客文章的对它的作用解释为用于将复杂对象的创建过程与其细节表示分离。但对于初学者来说&…...

Java之包,权限修饰符,final关键字详解
包 2.1 包 包在操作系统中其实就是一个文件夹。包是用来分门别类的管理技术,不同的技术类放在不同的包下,方便管理和维护。 在IDEA项目中,建包的操作如下: 包名的命名规范: 路径名.路径名.xxx.xxx // 例如ÿ…...
“深入解析JVM:Java虚拟机内部原理揭秘“
标题:深入解析JVM:Java虚拟机内部原理揭秘 摘要:本文将深入探讨Java虚拟机(JVM)的内部原理,包括JVM的架构、运行时数据区域、垃圾回收机制以及即时编译器等重要组成部分。通过对JVM内部原理的解析…...

Mac下Jmeter安装及基本使用
本篇文章只是简单的介绍下Jmeter的下载安装和最基本使用 1、初识Jmeter 前一段时间客户端app自测的过程中,有偶现请求某个接口返回数据为空的问题,领导让我循环100次请求这个接口,看看有没有结果为空的问题。听同事说有Jmeter的专业测试工具…...

云计算与边缘计算:加速数字化转型的关键驱动力
云计算和边缘计算技术正以惊人的速度改变着企业的业务和基础架构。这些先进的技术为企业带来了灵活性、可扩展性和成本效益的优势,重新定义了业务运作的方式。 云计算是通过互联网将计算资源提供给用户的一种服务模式。通过云计算,企业可以将应用程序、…...

TheGem主题 - 创意多用途和高性能WooCommerce WordPress主题/网站
TheGem主题概述 – 适合所有人的TheGem 作为设计元素、样式和功能的终极 Web 构建工具箱而设计和开发,TheGem主题将帮助您在几分钟内构建一个令人印象深刻的高性能网站,而无需触及一行代码。不要在编码上浪费时间,探索你的创造力!…...
Pytorch-day10-模型部署推理-checkpoint
模型部署&推理 模型部署模型推理 我们会将PyTorch训练好的模型转换为ONNX 格式,然后使用ONNX Runtime运行它进行推理 1、ONNX ONNX( Open Neural Network Exchange) 是 Facebook (现Meta) 和微软在2017年共同发布的,用于标准描述计算图的一种格式…...
vue使用websocket
建立websocket.js // 信息提示 import { Message } from element-ui // 引入用户id import { getTenantId, getAccessToken } from /utils/auth// websocket地址 var url ws://192.168.2.20:48081/websocket/message // websocket实例 var ws // 重连定时器实例 var tt // w…...

jmeter入门:接口压力测试全解析
一.对接口压力测试 1.配置 1.添加线程组(参数上文有解释 这里不介绍) 2.添加取样器 不用解释一看就知道填什么。。。 3.添加头信息(否则请求头对不上) 也不用解释。。。 4.配置监听器 可以尝试使用这几个监听器。 2.聚合结果…...

go、java、.net、C#、nodejs、vue、react、python程序问题进群咨询
1、面试辅导 2、程序辅导 3、一对一腾讯会议辅导 3、业务逻辑辅导 4、各种bug帮你解决。 5、培训小白 6、顺利拿到offer...
树莓派4B最新系统Bullseye 64 bit使用xrdp远程桌面黑屏卡顿问题
1、树莓派换源 打开源文件 sudo nano /etc/apt/sources.list注释原来的,更换为清华源 deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye main contrib non-free deb https://mirrors.tuna.tsinghua.edu.cn/debian/ bullseye-updates main contrib no…...
EasyExcel入门介绍及工具类,网络下载excel
前言:在这里分享自己第一次使用EasyExcel并且编写工具类,且在接口中支持excel文件下载的一系列流程,包含所有前后端(JSJAVA)完整代码,可以根据自己需要自行提取,仅供参考。 一.引入EasyExcel依赖…...

【HarmonyOS北向开发】-04 ArkTS开发语言-ArkTS基础知识
飞书原文档:Docs...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...

通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
Go 语言接口详解
Go 语言接口详解 核心概念 接口定义 在 Go 语言中,接口是一种抽象类型,它定义了一组方法的集合: // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的: // 矩形结构体…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?
论文网址:pdf 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记,谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...

【笔记】WSL 中 Rust 安装与测试完整记录
#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统:Ubuntu 24.04 LTS (WSL2)架构:x86_64 (GNU/Linux)Rust 版本:rustc 1.87.0 (2025-05-09)Cargo 版本:cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...

STM32---外部32.768K晶振(LSE)无法起振问题
晶振是否起振主要就检查两个1、晶振与MCU是否兼容;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容(CL)与匹配电容(CL1、CL2)的关系 2. 如何选择 CL1 和 CL…...
区块链技术概述
区块链技术是一种去中心化、分布式账本技术,通过密码学、共识机制和智能合约等核心组件,实现数据不可篡改、透明可追溯的系统。 一、核心技术 1. 去中心化 特点:数据存储在网络中的多个节点(计算机),而非…...

ui框架-文件列表展示
ui框架-文件列表展示 介绍 UI框架的文件列表展示组件,可以展示文件夹,支持列表展示和图标展示模式。组件提供了丰富的功能和可配置选项,适用于文件管理、文件上传等场景。 功能特性 支持列表模式和网格模式的切换展示支持文件和文件夹的层…...

CMS内容管理系统的设计与实现:多站点模式的实现
在一套内容管理系统中,其实有很多站点,比如企业门户网站,产品手册,知识帮助手册等,因此会需要多个站点,甚至PC、mobile、ipad各有一个站点。 每个站点关联的有站点所在目录及所属的域名。 一、站点表设计…...

2025 后端自学UNIAPP【项目实战:旅游项目】7、景点详情页面【完结】
1、获取景点详情的请求【my_api.js】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http(/login/getWXSessionKey, {code,avatar}); };//…...