侧边栏壁纸
博主头像
月伴飞鱼 博主等级

行动起来,活在当下

  • 累计撰写 126 篇文章
  • 累计创建 31 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录

Volatile关键字为什么可以保证可见性?他是如何实现的?

月伴飞鱼
2025-03-16 / 0 评论 / 1 点赞 / 7 阅读 / 0 字
温馨提示:
部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

volatile 关键字为什么可以保证可见性?

volatile 关键字可以保证变量的可见性,是通过 主存和线程缓存的同步机制 实现的。

具体来说,它通过 内存屏障缓存一致性协议 实现了线程间变量的可见性。

可见性问题的根源

在多线程程序中,每个线程会将共享变量从主存复制到自己的 工作内存(如 CPU 缓存)中操作。

这种机制虽然提高了性能,但可能导致线程之间看到的变量值不一致的问题(即可见性问题)。

volatile 如何保证可见性

1. 修改操作强制写入主存

  • 当一个线程修改 volatile 修饰的变量时,该变量的新值会立即刷新到主存

  • 其他线程会被通知其缓存中的该变量值无效,必须从主存重新加载。

2. 读取操作强制从主存加载

  • 当一个线程读取 volatile 修饰的变量时,它总是直接从主存中获取最新值,而不是从线程的本地缓存中读取。

实现机制

1. 内存屏障

volatile 的实现依赖于 内存屏障(Memory Barrier),这是一种指令,确保对变量的操作不会因编译器优化或 CPU 重排序而改变顺序。

  • 写屏障(Store Barrier)
    在写入 volatile 变量之后,会插入一个写屏障,强制将最新值写回主存。

  • 读屏障(Load Barrier)
    在读取 volatile 变量之前,会插入一个读屏障,确保从主存加载变量值。

2. 缓存一致性协议

现代 CPU 使用缓存一致性协议(如 MESI 协议)来确保多核处理器的缓存数据一致性。

  • 当一个线程修改 volatile 变量并刷新到主存时,其他线程缓存中该变量的值会被标记为 无效

  • 当其他线程试图访问该变量时,会发现缓存无效,从而重新从主存加载最新值。

代码示例

以下代码展示了 volatile 如何确保变量的可见性:

1. 不使用 volatile(可能导致可见性问题)

public class VisibilityWithoutVolatile {
    private boolean flag = true;

    public void writer() {
        flag = false; // 修改 flag 的值
    }

    public void reader() {
        while (flag) { 
            // 无限循环,可能无法感知 flag 的变化
        }
        System.out.println("Flag has been updated to false");
    }

    public static void main(String[] args) {
        VisibilityWithoutVolatile example = new VisibilityWithoutVolatile();

        Thread writerThread = new Thread(example::writer);
        Thread readerThread = new Thread(example::reader);

        readerThread.start();
        writerThread.start();
    }
}
  • 问题:线程可能会一直读取旧值,因为 flag 的更新未及时刷新到主存,或者另一个线程未从主存中加载最新值。

2. 使用 volatile(确保可见性)

public class VisibilityWithVolatile {
    private volatile boolean flag = true;

    public void writer() {
        flag = false; // 修改 flag 的值,立即刷新到主存
    }

    public void reader() {
        while (flag) { 
            // 读取时从主存获取最新值
        }
        System.out.println("Flag has been updated to false");
    }

    public static void main(String[] args) {
        VisibilityWithVolatile example = new VisibilityWithVolatile();

        Thread writerThread = new Thread(example::writer);
        Thread readerThread = new Thread(example::reader);

        readerThread.start();
        writerThread.start();
    }
}
  • 结果:程序能够正常终止,因为 volatile 确保了变量的可见性。

总结

  1. volatile 的可见性保证

    • 修改 volatile 变量时,会立即写入主存。

    • 读取 volatile 变量时,总是从主存读取。

  2. 实现机制

    • 内存屏障:防止重排序,确保操作顺序。

    • 缓存一致性协议:通知其他线程缓存失效,强制重新加载。

  3. 适用场景

    • 适合状态标志(如 boolean)等简单场景。

    • 不适合需要保证原子性的场景(如 i++)。

公众号.png

1
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin
    1. 支付宝打赏

      qrcode alipay
    2. 微信打赏

      qrcode weixin
博主关闭了所有页面的评论