Java多线程-volatile详解

本文探讨了缓存一致性问题在高速缓存方案中的重要性,涉及Intel的MESI协议,以及并发编程中的原子性、可见性和有序性。通过JMM和volatile关键字、synchronized和Lock的使用,解释了如何保证数据的一致性,以及AtomicInteger和Unsafe.CAS原子指令的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

缓存一致性问题
  • 起因:高速缓存
  • 方案
    • 总线LOCK#锁,释放锁前该块内存无法被别的cpu或线程访问
    • 缓存一致性协议(Intel的MESI协议),cpu写共享变量时,会通知其它cpu将缓存失效
并发性质
  • 原子性:事务、原语
  • 可见性:值在高速缓存中被改变,未来得及写入内存,别的线程去读就错了
  • 有序性(JVM指令重排)
    语句1和2被重排,下面的多线程可能就挂了

// 线程1 - 负责加载context,然后发通知
context = loadContext(); // 语句1
inited = true;           // 语句2

//线程2 - 监听inited通知信号,业务处理
while(!inited) {
    sleep();
}
doSomethingWithConfig(context);
JMM Java内存模型
  • 线程先将堆中对象属性拷到工作内存,修改,然后刷回堆
  • 原子性:synchronized/Lock
  • 可见性
    • volatile:读值时强制从主内存读;值改变后会马上刷入主内存
    • synchronized/Lock:出同步前刷入主内存
  • 有序性
    • volatile
      • 读写volatile变量时,前面操作已完成,后面操作未开始
      • 语句重排时无法突破volatile边界
      • 上面例子中,给inited属性加volatile即可
    • happens-before(先行发生)原则
      • 程序次序:单线程内,先写的代码先执行(这个是从结果角度出发,和指令重排不矛盾)
      • 锁定规则:同一个锁,必先unlock,再lock
      • volatile变量规则:一个线程先写,一个线程后读,那么必先写完
      • 传递规则:A先于B,B先于C,则A先于C
      • 线程启动:Thread的start方法是该线程的第一个动作
      • 线程中断:interrupt方法的调用先于被中断线程检测到中断信号
      • 线程终结:所有操作先于终止检测
      • 对象终结:对象初始化的结束,早于finalize方法的开始
实现机制
  • volatile会在汇编代码中增加一个lock前缀指令(相当于内存屏障)
    • 确保指令重排时不会把其后面的指令排到其之前,也不会把前面的指令排到内存屏障后面;执行到内存屏障指令时,前面操作均已完成
    • 强制将对缓存的修改立即写入主内存
    • 写操作会导致其他CPU中对应缓存无效
举例场景

// 只有volatile
public class Test {
    private volatile int inc = 0;
    public void increase() {
        inc++; // 线程1和2同时读入inc,线程2快速+1写回主内存;由于线程1已读过值,直接开加,所以错了
    }
    public static void main(String[] args) {
        final Test test = new Test();
        for(int i = 0; i < 10; i++) {
            new Thread(() -> {
                for(int j = 0; j < 1000; j++) {
                    test.increase();
                }
            }).start();
        }
        while(Thread.activeCount() > 1) {
            Thread.yield();
        }
        System.out.println(test.inc);
    }
}

// synchronized,阻塞了从主内存取值的动作
private int inc = 0;
public synchronized void increase() {
    inc++;
}

// Lock
private int inc = 0;
Lock lock = new ReentrantLock();
public void increase() {
    try {
        lock.lock();
        inc++;
    } finally {
        lock.unlock();
    }
}

// AtomicInteger
private AtomicInteger inc = new AtomicInteger();
public void increase() {
    inc.getAndIncrement();
}

public final int getAndIncrement() {
    return unsafe.getAndAddInt(this, valueOffset, 1);
}

public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
    do {
        var5 = this.getIntVolatile(var1, var2);
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

    return var5;
}
  • AtomicInteger -> Unsafe.CAS -> 处理器CMPXCHG指令(原子指令)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Eddy咸鱼

感谢大佬加鸡蛋~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值