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

volitale伪共享问题及解决方案

文章目录

      • 一、什么是伪共享?
      • 二、volatile与伪共享的关系
      • 三、伪共享的示例
      • 四、如何解决伪共享?
        • 1. 缓存行填充(Padding)
        • 2. Java 8的@Contended注解
      • 五、总结

在并发编程中,volatile关键字用于保证变量的可见性和禁止指令重排序,但它无法解决伪共享(False Sharing) 问题,甚至可能因volatile的内存语义加剧伪共享的性能影响。

一、什么是伪共享?

伪共享是CPU缓存机制导致的性能问题,根源在于CPU缓存行(Cache Line) 的工作方式:

  • CPU缓存以「缓存行」为最小存储单位(通常64字节),一个缓存行可以存储多个变量。
  • 当多个线程同时操作不同变量,但这些变量恰好位于同一缓存行时,会触发缓存一致性协议(如MESI),导致缓存行频繁失效和刷新,大幅降低性能。

简单说:无关变量共享了同一个缓存行,引发了不必要的缓存竞争,这就是伪共享。

二、volatile与伪共享的关系

volatile的内存语义会强化伪共享的影响:

  • volatile变量的修改会立即刷新到主存,并使其他CPU核心中该变量的缓存副本失效(通过缓存一致性协议)。
  • 若多个volatile变量位于同一缓存行,即使线程操作的是不同变量,也会因其中一个变量的修改导致整个缓存行失效,其他线程不得不重新从主存加载数据,产生额外开销。

三、伪共享的示例

假设两个线程分别修改VolatileData中的ab(均为volatile变量):

public class VolatileData {volatile long a;  // 8字节volatile long b;  // 8字节
}

由于ab总大小为16字节(远小于64字节缓存行),它们会被放入同一缓存行。此时:

  • 线程1修改a → 缓存行标记为失效 → 线程2读取b时,发现缓存行失效,必须从主存重新加载。
  • 线程2修改b → 缓存行再次失效 → 线程1读取a时又需重新加载。

这种频繁的缓存行失效会导致性能下降,这就是volatile变量引发的伪共享问题。

四、如何解决伪共享?

核心思路是让每个变量独占一个缓存行,避免多个变量共享缓存行。常见方案有两种:

1. 缓存行填充(Padding)

手动添加无用字段,填充缓存行剩余空间,使目标变量独占64字节缓存行。

示例(针对64字节缓存行,每个long占8字节):

public class VolatileDataWithPadding {volatile long a;// 填充7个long(56字节),加上a的8字节,共64字节,独占一个缓存行long p1, p2, p3, p4, p5, p6, p7;volatile long b;// 同样填充,让b独占另一个缓存行long p8, p9, p10, p11, p12, p13, p14;
}

此时ab分别位于不同缓存行,线程操作时不会相互影响。

2. Java 8的@Contended注解

Java 8引入@sun.misc.Contended注解(需JDK支持),自动为变量添加缓存行填充,无需手动写填充字段。

使用方式:

import sun.misc.Contended;public class VolatileDataWithContended {@Contended  // 自动填充,使a独占缓存行volatile long a;@Contended  // 自动填充,使b独占缓存行volatile long b;
}

注意:需添加JVM参数启用(默认仅用于JDK内部类):

-XX:-RestrictContended

五、总结

  • 伪共享是缓存行机制导致的性能问题,与变量是否为volatile无关,但volatile会加剧其影响。
  • 解决核心:让并发访问的变量各自独占一个缓存行(通过手动填充或@Contended)。
  • 适用场景:多线程高频读写不同变量的场景(如计数器、并发队列等),普通场景无需处理(会浪费内存)。

通过避免伪共享,可显著提升高并发场景下的性能(实测可能有10倍以上提升)。

http://www.dtcms.com/a/353845.html

相关文章:

  • SoC如何实现线程安全?
  • 【进阶篇第五弹】《详解存储过程》从0掌握MySQL中的存储过程以及存储函数
  • TypeScript:Interface接口
  • 如何启动一个分支网络改造试点?三步走
  • 【链表 - LeetCode】25. K 个一组翻转链表
  • 干眼症护理学注意事项
  • linux下的网络编程(2)
  • 技术分析 | Parasoft C/C++test如何突破单元测试的隔离难题
  • 亚马逊关键词策略全解析:类型、工具与多账号运营优化指南
  • AT_abc406_f [ABC406F] Compare Tree Weights
  • Windows/Linux 环境下 Jmeter 性能测试的安装与使用
  • 基于SpringBoot的宠物领养服务系统【2026最新】
  • MySQL 面试题系列(五)
  • Unity自定义Inspector面板之使用多选框模拟单选框
  • 前端技术演进录:从 AI 革命到架构新生
  • 【Linux】常用命令 拥有者和权限(四)
  • Python随机选择完全指南:从基础到高级工程实践
  • 安全向量模板类SiVector
  • vue 前端 区域自适应大小
  • AWS申请增加弹性IP配额流程
  • 《Vuejs设计与实现》第 17 章(编译优化)
  • 机器视觉学习-day05-图片颜色识别及颜色替换
  • # 快递单号查询系统:一个现代化的物流跟踪解决方案
  • YOLO12n-Deepsort多目标跟踪之昆虫数据集
  • 【C++标准库】<ios>详解基于流的 I/O
  • 科技赋能生态,智慧守护农林,汇岭生态开启农林产业现代化新篇章
  • C# OpenCVSharp 实现物体尺寸测量方案
  • Whisper JAX:突破性实时语音识别加速框架,性能提升70倍的开源解决方案
  • Spring : IOC / DI (控制反转 / 依赖注入)
  • C/C++---前缀和(Prefix Sum)