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

内存可见性和伪共享问题

文章目录

  • 什么是内存可见性问题
  • 为什么会出现可见性问题
  • 解决可见性问题的方法
    • 1. 使用volatile关键字
    • 2. 使用synchronized
    • 3. 使用java.util.concurrent包下的原子类
  • 什么是伪共享问题
  • CPU缓存行
  • 伪共享的危害
  • 解决伪共享的方法
    • 1. 缓存行填充
    • 2. 使用@Contended注解(JDK 8+)

什么是内存可见性问题

内存可见性问题指的是:一个线程对共享变量的修改,对其他线程不一定立即可见。这听起来很奇怪,但在现代多核处理器架构下,确实存在这个问题。

为什么会出现可见性问题

现代CPU为了提升性能,每个核心都有自己的缓存,多个核心共享L3缓存。当线程修改变量时,可能只是修改了CPU缓存中的副本,而没有立即写回主内存。

public class VisibilityExample {private boolean flag = false;private int counter = 0;public void writer() {counter = 1;        // 写操作1flag = true;        // 写操作2}public void reader() {if (flag) {         // 读操作1System.out.println(counter);  // 读操作2}}
}

在上面的例子中,即使flag为true,counter的值对读线程可能仍然不可见。

解决可见性问题的方法

1. 使用volatile关键字

public class VolatileExample {private volatile boolean flag = false;private volatile int counter = 0;// volatile确保修改立即对其他线程可见
}

2. 使用synchronized

public class SynchronizedExample {private boolean flag = false;private int counter = 0;public synchronized void writer() {counter = 1;flag = true;}public synchronized void reader() {if (flag) {System.out.println(counter);}}
}

3. 使用java.util.concurrent包下的原子类

public class AtomicExample {private final AtomicBoolean flag = new AtomicBoolean(false);private final AtomicInteger counter = new AtomicInteger(0);public void writer() {counter.set(1);flag.set(true);}public void reader() {if (flag.get()) {System.out.println(counter.get());}}
}

什么是伪共享问题

伪共享是指多个线程访问同一缓存行中的不同变量时,即使这些变量在逻辑上是独立的,但由于它们位于同一缓存行,一个线程的修改会导致其他线程的缓存行失效,从而引起不必要的缓存同步开销。

CPU缓存行

现代CPU的缓存是以缓存行为单位进行管理的,一般大小为64字节,相当于8个long类型的数据。当CPU访问内存时,会将包含目标地址的整个缓存行加载到缓存中。

伪共享的危害

public class FalseSharingExample {// 这两个变量很可能在同一缓存行中private volatile long variableA = 0L;private volatile long variableB = 0L;public void threadA() {for (int i = 0; i < 100000000; i++) {variableA++;  // 线程A修改variableA}}public void threadB() {for (int i = 0; i < 100000000; i++) {variableB++;  // 线程B修改variableB}}
}

在上面的例子中,虽然两个线程操作不同的变量,但由于变量可能在同一缓存行,每次修改都会导致对方的缓存行失效,导致线程读取变量的时候又要去内存中读取,性能大幅下降。

解决伪共享的方法

1. 缓存行填充

public class PaddingExample {private volatile long variableA = 0L;// 使用填充字节避免伪共享private long p1, p2, p3, p4, p5, p6, p7;private volatile long variableB = 0L;private long p8, p9, p10, p11, p12, p13, p14;
}

2. 使用@Contended注解(JDK 8+)

@sun.misc.Contended
public class ContendedExample {private volatile long variableA = 0L;@sun.misc.Contendedprivate volatile long variableB = 0L;
}

使用@Contended需要JVM参数:-XX:-RestrictContended

http://www.lryc.cn/news/619464.html

相关文章:

  • 元数据与反射:揭开程序的“自我认知”能力
  • 5.语句几个分类
  • AXIOS 入门
  • 6 ABP 框架中的事件总线与分布式事件
  • 超越相似名称:Elasticsearch semantic text 如何在简洁、高效、集成方面超越 OpenSearch semantic 字段
  • 深度学习-卷积神经网络-GoogLeNet
  • Perl——qw()函数
  • 【类与对象(下)】探秘C++构造函数初始化列表
  • [idekCTF 2025] diamond ticket
  • AAAI论文速递 | NEST:超图小世界网络让自动驾驶轨迹预测更精准
  • Java面试宝典:G1垃圾收集器下
  • C#面试题及详细答案120道(11-20)-- 面向对象编程(OOP)
  • AI抢饭碗,软件测试该何去何从?
  • TraeCN与Cursor对比分析:双雄争锋下的AI编程工具演进之路
  • Vue3 中 <script setup> 场景下,需要手动导入和不需要手动导入的内容整理
  • 第二十二天:指针与内存
  • TF - IDF算法面试与工作常见问题全解析
  • OpenCV常见问题汇总
  • 音视频处理新纪元:12款AI模型的语音转录和视频理解能力横评
  • 【计算机网络】王道考研笔记整理(4)网络层
  • OpenAI 回应“ChatGPT 用多了会变傻”
  • Debian新一代的APT软件源配置文件格式DEB822详解
  • 【C++详解】用红黑树封装模拟实现mymap、myset
  • 《论文阅读》从特质到移情:人格意识多模态移情反应生成 ACL 2025
  • 2025 环法战车科技对决!维乐 Angel Glide定义舒适新标
  • 用vscode开发和调试golang超简单教程
  • 【debian系统】cuda13和cudnn9.12详细安装步骤
  • Pytest项目_day15(yaml)
  • 肖臻《区块链技术与应用》第十二讲:比特币是匿名的吗?—— 深入解析匿名性、隐私风险与增强技术
  • 《算法导论》第 22 章 - 基本的图算法