Java并发编程之有序性

一、有序性

1. 什么是有序性?

有序性指程序执行的顺序与代码的先后顺序一致。但在实际执行中,为了提高性能,编译器处理器可能会对指令进行重排序。

2. 为什么需要有序性?

若线程A和线程B对共享变量的操作顺序被重排,可能导致以下问题:

  • 可见性问题:线程A的修改未及时对其他线程可见。
  • 逻辑错误:程序依赖的操作顺序被破坏,如单例模式的双重检查锁定。

二、指令重排序

int a = 1;
int b = 2;
可能的执行顺序:

编译器或处理器可能将b = 2提前到a = 1之前执行,但不会影响单线程结果。


三、volatile的禁止重排序机制

1. volatile的内存语义

volatile通过以下两种方式保证有序性:

  1. 禁止重排序:限制编译器和处理器对volatile变量相关指令的重排序。
  2. 内存屏障:插入特定屏障指令,确保操作顺序符合预期。

2. volatile变量的读写规则

  • 写操作
    在写volatile变量后插入StoreStore屏障(禁止之前的普通写与volatile写重排序)和StoreLoad屏障(确保写操作全局可见)。

  • 读操作
    在读volatile变量前插入LoadLoad屏障(禁止之后的普通读与volatile读重排序)和LoadStore屏障(禁止之后的普通写与volatile读重排序)。


四、实战案例:双重检查锁定

1. 问题代码(无volatile

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

2. 隐患分析

对象初始化操作instance = new Singleton()可能被重排序为:

  1. 分配内存空间。
  2. 将引用instance指向内存空间(此时instance != null)。
  3. 初始化对象(执行构造函数)。

若步骤2和3被重排,其他线程可能拿到未初始化的对象。

3. 解决方案:使用volatile

private static volatile Singleton instance; // 添加volatile
  • 禁止重排序:确保对象的初始化操作完成后,才将引用赋值给instance
  • 内存可见性:保证其他线程能立即看到instance的最新值。

五、volatile的局限性

场景volatile是否适用原因
复合操作(如i++)不适用volatile无法保证原子性
多变量依赖的原子操作不适用需使用锁或原子类(如AtomicInteger
简单的状态标志适用适合单个变量的可见性控制
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值