Java 并发探秘:JCTools 源码剖析,为什么 Netty 放弃 JDK 自带队列而选择 MpscArrayQueue?

标签:#Java #Netty #JCTools #Concurrency #Performance #FalseSharing


🐢 前言:JDK 队列的痛点

在 Netty 的 Reactor 线程模型中,EventLoop本质上是一个单线程的执行器。
它需要处理两类任务:

  1. IO 事件:来自网络 Socket 的读写。
  2. 外部任务:其他线程提交给 EventLoop 执行的任务(例如channel.write())。

这是一个典型的多生产者-单消费者 (Multi-Producer Single-Consumer, MPSC)场景。

如果你使用 JDK 的ArrayBlockingQueue

  • 锁竞争严重puttake共用一把锁(或者两把锁),导致线程上下文切换频繁。
  • 伪共享 (False Sharing):高并发下,不同线程修改相邻的变量,导致 CPU 缓存失效。

🧱 一、 什么是伪共享 (False Sharing)?

这是本文最核心的爆点。要读懂 JCTools,必须先懂 CPU 缓存架构。

CPU 不是直接读写内存的,而是通过 L1/L2/L3 缓存。缓存的最小单位是缓存行 (Cache Line),通常是64 字节
如果变量A和变量B恰好在同一个 Cache Line 中:

  1. Core 1 修改了A
  2. Core 2 想要读取B
  3. 根据缓存一致性协议 (MESI),Core 1 修改A会导致整个 Cache Line 失效。
  4. Core 2 必须重新从主存加载 Cache Line,尽管它根本不关心A

这就是伪共享——两个线程明明没有共享数据,却像是在争抢同一个变量一样,导致性能断崖式下跌。

伪共享示意图 (Mermaid):

CPU_Core_2

CPU_Core_1

修改 Head

Invalidate (失效通知)

读取 Tail

Cache Miss (缓存未命中)

主内存 (Cache Line: 64 Bytes)

[ Head (8B) | Tail (8B) | ... 其他数据 ... ]

L1 Cache

线程 1: 修改 Head 指针

L1 Cache

线程 2: 修改 Tail 指针


🛡️ 二、 JCTools 的黑魔法:缓存行填充 (Padding)

打开MpscArrayQueue的继承结构,你会看到一堆看似“废话”的类定义。这些类存在的唯一意义,就是为了填充字节,把关键变量隔离开。

1. 源码赏析
// 1. 用于填充 Head 之前的空间abstractclassMpscArrayQueueL1Pad<E>extendsConcurrentCircularArrayQueue<E>{longp00,p01,p02,p03,p04,p05,p06,p07;longp10,p11,p12,p13,p14,p15,p16;}// 2. 存放 Producer Index (生产者索引)abstractclassMpscArrayQueueProducerIndexField<E>extendsMpscArrayQueueL1Pad<E>{protectedvolatilelongproducerIndex;}// 3. 又是填充!把 Producer Index 和 Consumer Index 隔离开abstractclassMpscArrayQueueMidPad<E>extendsMpscArrayQueueProducerIndexField<E>{longp20,p21,p22,p23,p24,p25,p26;longp30,p31,p32,p33,p34,p35,p36,p37;}// 4. 存放 Consumer Index (消费者索引)abstractclassMpscArrayQueueConsumerIndexField<E>extendsMpscArrayQueueMidPad<E>{protectedlongconsumerIndex;}

解析:

  • long占 8 字节。
  • p00p16一共 15 个 long,加上对象头,足以填满 128 字节(两个 Cache Line)。
  • 结果producerIndexconsumerIndex永远不可能出现在同一个 Cache Line 中。生产者狂写producerIndex时,绝对不会干扰消费者读取consumerIndex

⚡ 三、 极致的入队与出队:位运算与 Unsafe

除了 Padding,JCTools 在算法层面也做到了极致。

1. 环形数组的索引计算

普通队列计算下标用取模:index = i % capacity除法运算在 CPU 中是很慢的。
JCTools 强制要求队列容量必须是2 的 N 次幂
优化后:index = i & (capacity - 1)位运算比除法快几十倍。

2. 入队 (offer) - 多生产者 CAS

因为是多生产者(Netty 中可能有多个线程同时向 EventLoop 提交任务),所以入队需要保证线程安全。
JCTools 使用Unsafe.getAndAddLong(CAS) 来移动producerIndex,而不是加锁。

// 简化版源码逻辑publicbooleanoffer(Ee){longmask=this.mask;longcapacity=mask+1;longpIndex;do{pIndex=lvProducerIndex();// Load Volatile// 检查队列是否已满...}while(!casProducerIndex(pIndex,pIndex+1));// CAS 抢占位置// 计算数组偏移量longoffset=calcCircularRefElementOffset(pIndex,mask);// 将元素放入数组 (Ordered Store, 比 Volatile 写更轻量)soElement(buffer,offset,e);returntrue;}
3. 出队 (poll) - 单消费者无锁

重点来了!因为是Single Consumer,只有一个线程(EventLoop)会去取数据。
所以poll方法完全不需要锁,甚至不需要 CAS!
它只需要普通的内存读取和写入,配合lazySet(StoreStore 内存屏障) 即可。这是 MPSC 模型相比 JDKArrayBlockingQueue(MPMC) 最大的优势。


📊 四、 性能对比

在基准测试(JMH)中,MpscArrayQueue在高并发下的吞吐量通常是ArrayBlockingQueue3 到 5 倍

特性ArrayBlockingQueue (JDK)MpscArrayQueue (JCTools)
锁机制ReentrantLock (全局锁)CAS + 无锁
伪共享存在 (需手动优化)完全解决 (Padding)
索引计算取模 (%)位运算 (&)
适用场景通用场景多生产者单消费者 (Reactor 模型)
扩容不支持不支持 (有 MpscChunkedArrayQueue 支持)

🎯 总结

Netty 选择 JCTools 并非偶然,而是对性能极致追求的必然结果。
MpscArrayQueue教会了我们三件事:

  1. 场景化优化:通用组件(JDK Queue)为了兼容性牺牲了性能,特定场景(MPSC)可以用特定数据结构降维打击。
  2. 硬件亲和性:写高性能代码不能只看语法,还要看 CPU 缓存(伪共享)和指令集(位运算)。
  3. Unsafe 的艺术:Java 的Unsafe类虽然危险,但它是通往性能之巅的必经之路。

Next Step:
检查你项目中是否有使用LinkedBlockingQueue做线程池任务队列的场景?如果你的消费者线程只有一个(或者你可以按 ID哈希分片成单消费者),尝试引入JCTools替换它,你的系统吞吐量可能会有惊喜。

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

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

相关文章

todo

todo作者很懒,所以经常会立flag,这里是他写下的todo list QAQtodo 创建时间 状态埃氏筛分块筛法 26/1/18 未完成

Go 调度器 (GMP) 揭秘:从汇编角度看 Goroutine 是如何实现“协程切换”的?

标签&#xff1a; #Go #Golang #GMP #Assembly #Runtime #Concurrency&#x1f680; 前言&#xff1a;GMP 的本质是“复用” 操作系统线程&#xff08;OS Thread&#xff09;太重了。创建一个线程需要 1-8MB 栈内存&#xff0c;切换一次需要进入内核态&#xff0c;耗时 1-2 微秒…

【创新未发表】基于matlab鸡群算法CSO和自适应双种群协同鸡群算法ADPCCSO无人机避障三维航迹规划【含Matlab源码 14980期】

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到海神之光博客之家&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49…

第 174 场双周赛Q2——3810. 变成目标数组的最少操作次数

题目链接&#xff1a;3810. 变成目标数组的最少操作次数&#xff08;中等&#xff09; 算法原理&#xff1a; 解法&#xff1a;模拟 38ms击败11.30% 时间复杂度O(N) ①先计算出哪些是需要修改的 ②统计需要修改的下标 ③原数组中相同的数可以一起修改&#xff0c;所以只要统计不…

【无人机三维路径规划】基于matlab鸡群算法CSO和自适应双种群协同鸡群算法ADPCCSO复杂山地模型下无人机路径规划【含Matlab源码 14981期】

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到海神之光博客之家&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49…

Day22-20260118

摘要 本文实现了一个基于Java的交互式计算器程序,具有加减乘除四种运算功能。程序通过定义四个独立的方法(add、subtract、multiply、divide)实现基本运算,采用while循环和switch结构实现用户交互界面。用户可以循环…

强烈安利9个AI论文写作软件,专科生搞定毕业论文!

强烈安利9个AI论文写作软件&#xff0c;专科生搞定毕业论文&#xff01; 论文写作的救星&#xff0c;AI 工具如何改变你的学术之路 对于专科生来说&#xff0c;毕业论文可能是大学生活中最令人头疼的一关。从选题、查资料到撰写、修改&#xff0c;每一步都充满了挑战。而如今&a…

【雷达跟踪】基于matlab面向目标跟踪的雷达干扰方法:提升航空器战场生存力的关键技术【含Matlab源码 14983期】复现含文献

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到海神之光博客之家&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49…

【雷达相控阵】毫米波相控阵中空间Zadoff-Chu调制快速波束对准【含Matlab源码 14977期】

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;Matlab领域博客之家&#x1f49e;&…

卷积神经网络的开端:$LeNet-5$

卷积神经网络的开端:\(LeNet-5\)[!NOTE] LeNet-5神经网络是1998年YANN LECUN等人在论文Gradient-Based Learning Applied to Document Recognition中提出的一种颠覆性的算法。 说实在的该方法本来是用来解决手写字母也…

【雷达相控阵】基于matlab毫米波相控阵中空间Zadoff-Chu调制快速波束对准【含Matlab源码 14977期】

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到海神之光博客之家&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49…

【心电信号ECG】SVM心电图心搏检测与分类【含Matlab源码 14982期】复现含文献

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;Matlab领域博客之家&#x1f49e;&…

【心电信号ECG】基于matlab SVM心电图心搏检测与分类【含Matlab源码 14982期】复现含文献

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到海神之光博客之家&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49…

【心血管疾病】心脏病数据集Kaggle医学特征二元分类预测心血管疾病【含Matlab源码 14984期】含报告

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;Matlab领域博客之家&#x1f49e;&…

社交网络数据科学:完整项目实战指南

社交网络数据科学&#xff1a;完整项目实战指南 引言 痛点引入&#xff1a;你可能遇到的「社交网络分析困境」 作为数据科学爱好者&#xff0c;你是否曾有过这样的困惑&#xff1a; 想学社交网络分析&#xff0c;但看着「图论」「中心性」「社区发现」等术语望而却步&#…

Hive与DynamoDB集成:云原生大数据方案

Hive与DynamoDB集成:云原生大数据方案 关键词:Hive、DynamoDB、云原生、大数据集成、数据处理 摘要:本文聚焦于Hive与DynamoDB的集成,旨在探讨云原生环境下的大数据解决方案。首先介绍了Hive和DynamoDB的背景信息,包括它们的特点和适用场景。接着详细阐述了两者集成的核心…

【创新未发表】鸡群算法CSO和自适应双种群协同鸡群算法ADPCCSO无人机避障三维航迹规划【含Matlab源码 14980期】

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;Matlab领域博客之家&#x1f49e;&…

Flutter × OpenHarmony 跨端汇率转换应用:货币数据模型与页面实现

文章目录Flutter OpenHarmony 跨端汇率转换应用&#xff1a;货币数据模型与页面实现前言背景Flutter OpenHarmony 跨端开发介绍开发核心代码代码解析心得总结Flutter OpenHarmony 跨端汇率转换应用&#xff1a;货币数据模型与页面实现 前言 在全球化经济背景下&#xff0c;…

【无人机三维路径规划】鸡群算法CSO和自适应双种群协同鸡群算法ADPCCSO复杂山地模型下无人机路径规划【含Matlab源码 14981期】

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;Matlab领域博客之家&#x1f49e;&…

【雷达跟踪】面向目标跟踪的雷达干扰方法:提升航空器战场生存力的关键技术【含Matlab源码 14983期】复现含文献

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;&#x1f49e;Matlab领域博客之家&#x1f49e;&…