两个线程即使更新不同字段,也可能因字段落在同一缓存行而互相使缓存失效,这叫伪共享。它是硬件与对象布局共同产生的 性能问题,只能通过目标机器上的可靠基准确认,不能靠字段相邻就下结论。
Table of contents
Open Table of contents
运行实验框架
下载伪共享实验样例下载, 运行:
java -cp target/classes dev.elaine.concurrency.FalseSharingSample
两个平台线程同时更新同一对象中的两个 volatile long,程序输出最终值和耗时。这个结果只证明样例执行完成,不是基准
结论:JIT、字段布局、CPU 拓扑、频率调整和系统负载都会影响单次数字。
缓存行为什么会竞争
CPU 通常按缓存行在核心之间传递一致性状态,而不是按 Java 字段。若核心 A 写 left,核心 B 写同一缓存行中的 right,
整行所有权会来回转移。业务变量没有共享,硬件传输单元却共享,因此称为“伪”共享。
volatile 还要求特定可见性和有序性,不等于每次操作直接访问主内存。实际协议由 CPU 架构和 JVM 生成代码决定。
怎样验证和修复
用 JMH 建立两组实现:字段可能相邻的版本,以及经过填充或分离对象的版本。配置多个 Fork、足够预热、固定核心或记录 CPU 拓扑,并同时观察吞吐和硬件缓存事件。
可选修复包括:
- 把热点写入拆到独立对象或分片计数器。
- 批量聚合,降低共享字段写入频率。
- 在 JDK 内部或受控场景使用
@Contended,并明确所需 JVM 开关。 - 重新设计所有权,让单一线程更新状态。
手工添加若干 long 填充不保证跨 JDK 字段布局稳定,也会增加内存占用。
Disruptor 解决什么问题
LMAX Disruptor 是第三方低延迟并发库。它使用预分配环形缓冲区、序号协调和等待策略,让生产者与消费者减少分配和锁 竞争。它适合事件结构固定、吞吐和尾延迟要求高、团队能管理等待策略与消费拓扑的场景。
Disruptor 不是通用消息队列:它不自动提供跨进程持久化、消费者离线恢复或网络传输。普通业务任务先使用有界
BlockingQueue,测得瓶颈后再比较 Disruptor。
下一步
阅读虚拟线程、固定与观测,用更轻量的线程承载大量阻塞式任务。