volatile与内存屏障

  1. volatile关键字的作用
    1.可见性保证
  • 问题场景:多线程环境下,线程对共享变量的修改可能对其他线程不可见(因CPU缓存一致性协议失效或编译器优化)。
  • 解决方案:volatile修饰的变量每次读写直接操作主内存,绕过线程本地缓存。
  • 代码示例
    volatile boolean flag = true;
    // 线程A
    flag = false;
    // 线程B
    while(flag); // 立即感知到flag的修改

2.禁止指令重排序

  • 问题场景:编译器和CPU为提高性能可能对指令重排序,导致多线程程序行为异常(如单例模式DCL失效)。
  • 解决方案:通过插入内存屏障禁止特定类型的重排序。

  1. 内存屏障类型与作用
    屏障类型作用描述对应JVM指令LoadLoad禁止Load操作之间的重排序load1; LoadLoad; load2StoreStore禁止Store操作之间的重排序store1; StoreStore; store2LoadStore禁止Load与后续Store操作重排序load; LoadStore; storeStoreLoad禁止Store与后续Load操作重排序(全能屏障)store; StoreLoad; load
  • volatile写操作:插入StoreStore + StoreLoad屏障
  • volatile读操作:插入LoadLoad + LoadStore屏障

  1. volatile与JMM(Java内存模型)
    1.happens-before规则
  • volatile变量规则:对volatile变量的写操作happens-before后续对该变量的读操作。

  • 传递性规则:若操作A happens-before B,B happens-before C,则A happens-before C。
    2.DCL(双重检查锁定)单例模式

  • 错误实现(无volatile):
    class Singleton {
    private static Singleton instance;
    private Singleton() {}
    public static Singleton getInstance() {
    if (instance == null) { // 第一次检查
    synchronized (Singleton.class) { // 加锁
    if (instance == null) { // 第二次检查
    instance = new Singleton(); // 可能发生指令重排序
    }
    }
    }
    return instance;
    }
    }

  • 问题分析:instance = new Singleton() 可能被重排序为:

i.分配内存空间
ii.将引用写入instance(此时instance != null)
iii.初始化对象

  • 正确实现(添加volatile):
    private static volatile Singleton instance; // 禁止指令重排序

  1. 硬件层面实现(以x86为例)
  • volatile写操作:通过LOCK前缀指令(如LOCK ADD)实现缓存一致性(MESI协议)和内存屏障效果。
  • volatile读操作:普通Load指令(x86强内存模型下Load操作自带屏障效果)。

  1. 高频面试题
    1.volatile能保证原子性吗?为什么?
  • 答案:不能。例如volatile int i = 0; i++在多线程下仍非原子操作,需用AtomicInteger或synchronized。
    2.volatile如何解决DCL单例问题?
  • 答案:通过禁止new Singleton()的指令重排序,确保对象初始化完成后再写入引用。
    3.volatile和synchronized的区别?
  • 对比维度

维度volatilesynchronized原子性无有(锁机制)可见性有有重排序禁止指令重排序临界区内代码不重排序适用场景单变量状态标记多操作原子性控制
4.内存屏障与CPU缓存的关系?

  • 答案:内存屏障确保缓存一致性协议(如MESI)的正确执行,强制刷新或失效缓存行。

  1. 实战:验证指令重排序
    public class ReorderExample {
    private static int x = 0, y = 0;
    private static int a = 0, b = 0;

    public static void main(String[] args) throws InterruptedException {
    for (int i = 0; ; i++) {
    x = y = a = b = 0;
    Thread t1 = new Thread(() -> {
    a = 1;
    x = b; // 此处可能重排序为:x = b; a = 1;
    });
    Thread t2 = new Thread(() -> {
    b = 1;
    y = a; // 此处可能重排序为:y = a; b = 1;
    });
    t1.start();
    t2.start();
    t1.join();
    t2.join();
    if (x == 0 && y == 0) { // 出现则证明发生重排序
    System.out.println(“第” + i + “次发生重排序”);
    break;
    }
    }
    }
    }

  • 运行结果:在高并发场景下可能输出重排序事件(概率极低,但存在可能)。

  • 关联 JMM Java内存模型完整规则与happens-before链
  • 关联 原子类 CAS与AtomicInteger底层实现
  • 其他
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值