volatile 的作用

想象一个家庭有一个共用的留言板:

  1. 没有 volatile 的情况(可能出现可见性问题):
public class FamilyBoard {
    private boolean hasNewMessage = false;  // 没有volatile修饰
    
    // 父母写留言
    public void leaveMessage() {
        hasNewMessage = true;  // 孩子可能看不到这个更新
        System.out.println("留言:记得买菜");
    }
    
    // 孩子查看留言
    public void checkMessage() {
        // 可能永远看不到新留言,因为使用的是自己CPU缓存中的旧值
        if (hasNewMessage) {
            System.out.println("收到新留言");
        }
    }
}
  1. 使用 volatile 确保可见性:
public class FamilyBoard {
    private volatile boolean hasNewMessage = false;  // 使用volatile修饰
    
    // 父母写留言
    public void leaveMessage() {
        hasNewMessage = true;  // 修改会立即被所有人看到
        System.out.println("留言:记得买菜");
    }
    
    // 孩子查看留言
    public void checkMessage() {
        // 一定能看到最新的留言状态
        if (hasNewMessage) {
            System.out.println("收到新留言");
        }
    }
}
  1. volatile 不保证原子性的例子:
public class MessageCounter {
    private volatile int messageCount = 0;  // 即使用volatile修饰也不保证原子性
    
    public void addMessage() {
        messageCount++;  // 这是复合操作:读取、加一、写入,不是原子的
    }
    
    // 需要用synchronized或AtomicInteger来确保原子性
    public synchronized void addMessageSafely() {
        messageCount++;
    }
}
  1. volatile 禁止指令重排的例子:
public class MessageSystem {
    private volatile boolean initialized = false;
    private String message;
    
    public void init() {
        message = "重要通知";      // 1
        initialized = true;        // 2
    }
    
    public void readMessage() {
        if (initialized) {         // 3
            System.out.println(message);  // 4
        }
    }
}

在这个例子中:

  • 没有 volatile:1和2的顺序可能被重排,导致其他线程看到 initialized 为 true 但 message 还未初始化
  • 使用 volatile:保证 2 一定在 1 之后执行,其他线程看到 initialized 为 true 时,message 一定已经被初始化

实际应用场景:

  1. 状态标志:
public class TaskManager {
    private volatile boolean shutdownRequested = false;
    
    public void shutdown() {
        shutdownRequested = true;
    }
    
    public void doWork() {
        while (!shutdownRequested) {
            // 执行任务
        }
    }
}
  1. 单例模式的双重检查锁定:
public class Singleton {
    private static volatile Singleton instance;  // volatile确保可见性和禁止重排
    
    public static Singleton getInstance() {
        if (instance == null) {  // 第一次检查
            synchronized (Singleton.class) {
                if (instance == null) {  // 第二次检查
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

使用建议:

  1. 使用 volatile 当:

    • 变量被多个线程共享
    • 变量的新值不依赖旧值
    • 只需要保证可见性,不需要保证原子性
  2. 不要使用 volatile 当:

    • 需要保证操作的原子性(使用 synchronized 或 atomic 类)
    • 变量值的更新依赖于当前值

通过这些例子,能更好地理解 volatile 的作用,它就像是在变量上贴了一个"立即生效"的标签,确保所有线程都能看到最新的值,但不能保证复杂操作的原子性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值