导读:本期聚焦于小伙伴创作的《如何利用 volatile 关键字保证多线程间的变量可见性》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《如何利用 volatile 关键字保证多线程间的变量可见性》有用,将其分享出去将是对创作者最好的鼓励。

在多线程并发执行的环境中,每个线程都有自己的工作内存,线程操作共享变量时会先从主内存拷贝副本到工作内存,操作完成后再写回主内存,这个机制就可能导致不同线程之间无法及时感知共享变量的最新值,也就是可见性问题。volatile关键字就是Java提供的一种轻量级的同步机制,专门用于解决这类可见性问题。

如何利用 volatile 关键字保证多线程间的变量可见性

多线程可见性问题的产生原因

Java内存模型(JMM)规定所有共享变量都存储在主内存中,每个线程有自己的工作内存,工作内存保存了线程使用到的变量的主内存副本。线程对变量的所有操作都必须在工作内存中进行,不能直接读写主内存的变量。当线程A修改了共享变量的值,还没来得及写回主内存时,线程B从主内存读取的还是旧值,这就产生了可见性问题。

我们可以通过一个简单的示例来复现这个问题:

public class VisibilityTest {
    // 未使用volatile修饰的共享变量
    private static boolean flag = false;

    public static void main(String[] args) throws InterruptedException {
        // 线程A修改flag的值
        Thread threadA = new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            flag = true;
            System.out.println("线程A将flag修改为true");
        });

        // 线程B循环判断flag的值
        Thread threadB = new Thread(() -> {
            while (!flag) {
                // 空循环,等待flag变为true
            }
            System.out.println("线程B感知到flag变为true,退出循环");
        });

        threadB.start();
        Thread.sleep(100);
        threadA.start();
    }
}

运行上述代码会发现,线程A修改flag为true之后,线程B可能一直无法退出循环,就是因为线程B无法及时感知到flag的最新值。

volatile关键字的可见性保证原理

volatile关键字通过以下三个层面的机制保证多线程间的变量可见性:

1. 基于JMM的变量读写规则

当一个变量被volatile修饰后,线程对它的写操作会立即刷新到主内存中,同时会让其他线程工作内存中该变量的副本失效。其他线程再次读取这个变量时,必须从主内存重新加载最新的值,这样就能保证所有线程看到的都是变量的最新状态。

2. 内存屏障的插入

volatile的底层实现依赖于内存屏障(Memory Barrier),JMM会在volatile变量的读写操作前后插入特定的内存屏障:

  • 在volatile写操作前插入StoreStore屏障,保证写操作之前的所有普通写操作都已经刷新到主内存
  • 在volatile写操作后插入StoreLoad屏障,保证写操作的结果对其他线程的读操作可见
  • 在volatile读操作后插入LoadLoad屏障和LoadStore屏障,保证读操作之后的普通读写操作不会重排序到读操作之前,且能读到最新的值

3. happens_before规则的支持

JMM的happens_before规则中明确规定,对一个volatile变量的写操作happens_before后续对这个volatile变量的读操作。也就是说,线程A对volatile变量的写操作的所有结果,对之后线程B读取这个volatile变量的操作都是可见的,这从规则层面保证了可见性。

volatile保证可见性的代码示例

我们修改之前的示例,给flag变量加上volatile修饰:

public class VolatileVisibilityTest {
    // 使用volatile修饰共享变量
    private static volatile boolean flag = false;

    public static void main(String[] args) throws InterruptedException {
        // 线程A修改flag的值
        Thread threadA = new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            flag = true;
            System.out.println("线程A将flag修改为true");
        });

        // 线程B循环判断flag的值
        Thread threadB = new Thread(() -> {
            while (!flag) {
                // 空循环,等待flag变为true
            }
            System.out.println("线程B感知到flag变为true,退出循环");
        });

        threadB.start();
        Thread.sleep(100);
        threadA.start();
    }
}

运行修改后的代码,线程A修改flag为true之后,线程B会立刻感知到变化并退出循环,这就是volatile保证可见性的实际效果。

volatile的使用注意事项

虽然volatile可以保证可见性,但它无法保证原子性,对于复合操作比如i++这类操作,即使变量用volatile修饰,在多线程下还是会出现问题,这类场景需要配合synchronized或者Lock等同步机制使用。另外volatile适合用来修饰状态标记量、双重检查锁定的单例模式中的实例变量等场景,不要滥用。

volatile多线程可见性JMM内存屏障happens_before修改时间:2026-06-20 05:33:28

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。