☰
最通俗易懂的 volatile 关键字详解,看完不懂你打我!2万字详解
2026/10/1 17:18:11 网站建设 项目流程

1. 开篇:从一个“诡异”的程序说起

很多 Java 初学者第一次接触 volatile 关键字,都是在面试题或者并发编程的文章里。看到它的第一反应往往是:“这不就是让变量在多个线程之间可见吗?好像和 synchronized 差不多吧?”结果真正写代码时,程序还是会时不时抽风,要么读到旧值,要么计数少了几百次。

为了让你真正搞懂 volatile,我们先不急着背概念,先看一段代码。假设有这样一个场景:一个线程在不停地修改一个开关变量,另一个线程根据这个开关决定要不要停止执行。很多人会写出下面这种代码:

public class StopFlagDemo { private static boolean stop = false; public static void main(String[] args) throws InterruptedException { Thread worker = new Thread(() -> { int count = 0; while (!stop) { count++; } System.out.println("worker 线程结束,count = " + count); }); worker.start(); Thread.sleep(1000); stop = true; System.out.println("主线程已经把 stop 设置为 true"); } }

按照正常的思维,主线程休眠 1 秒后把 stop 改成 true,worker 线程应该马上退出循环并打印结果。但如果你真的跑这段代码,很可能会出现一个尴尬的现象:主线程已经打印了“stop 设置为 true”,worker 线程却还在死循环,程序永远不会结束。

问题就出在 stop 这个变量上。worker 线程并没有在主线程修改 stop 之后“看到”这个最新值。换句话说,stop 这个变量在多线程环境下出现了可见性问题。而我们今天的主角 volatile,恰恰就是用来解决这一类问题的。

把 stop 变量的声明改成下面这样:

private static volatile boolean stop = false;

再次运行,worker 线程就能正常感知到 stop 的变化,程序可以顺利结束。这个例子虽然简单,但它背后藏着 volatile 最核心的价值:保证变量在多个线程之间的可见性,并且阻止一定的指令重排序。

接下来,这篇文章会用最通俗的语言,从内存模型、可见性、有序性、原子性、底层实现、使用场景、常见误区等多个维度,把 volatile 讲深、讲透。即使你之前对 JMM、内存屏障这些词一头雾水,读完以后也能给别人讲清楚 volatile 到底是什么、能干什么、不能干什么。

2. 为什么会有 volatile:先理解多线程的三个麻烦

要想真正明白 volatile 为什么存在,我们必须先搞清楚多线程编程里的三个经典问题:可见性、原子性和有序性。这三个问题就像三座大山,几乎所有并发 bug 都能归结到它们身上。

2.1 可见性:你改了,别人不一定看得到

可见性指的是:一个线程修改了共享变量之后,另一个线程能不能立刻读到修改后的新值。在我们的 StopFlagDemo 例子中,主线程修改了 stop,但 worker 线程没有看到,这就是可见性问题。

为什么会出现这种情况?因为现代计算机为了提高运行速度,不会让 CPU 每次都去主内存里读取变量。每个 CPU 都有自己的高速缓存,线程运行时很可能把变量缓存到自己的 CPU 缓存里。一个线程修改了自己缓存里的值,如果没及时同步回主内存,其他线程读取的还是自己缓存里的旧值。

在 Java 内存模型里,这种“缓存”抽象为每个线程拥有自己的工作内存。所有变量都存储在主内存中,线程要操作变量,必须先从主内存拷贝一份到自己的工作内存,操作完成后再写回主内存。如果两个线程各自持有同一变量的副本,又没有合适的同步机制,就可能读到过期数据。

volatile 变量具备可见性保证。当一个线程写 volatile 变量时,会立即把新值刷新回主内存;当另一个线程读 volatile 变量时,会强制从主内存重新读取最新值。这样就避免了各线程各读各的缓存副本。

2.2 原子性:看似一行代码,实际分好几步

原子性指的是:一个操作要么全部执行完成,要么全部不执行,中间不会被其他线程打断。很多人误以为“一行代码就是原子的”,这其实是并发编程里最常见的误区。

比如 i++ 这行代码,它看起来只有一步,实际在底层至少包含三个步骤:读取 i 的当前值,把当前值加 1,把结果写回 i。如果有两个线程同时执行 i++,可能都读到了同一个旧值,各自加 1 后写回,最终 i 可能只增加了 1,而不是我们期望的 2。

这就是典型的原子性问题。需要特别强调的一点是:volatile 并不保证原子性。也就是说,即便你把 i 声明成 volatile,两个线程同时执行 i++,依然可能丢失更新。这个问题我们后文会重点展开,它是很多人用错 volatile 的根源。

2.3 有序性:代码不是从上到下执行的

有序性指的是:程序执行的顺序是否和代码书写顺序一致。直觉告诉我们,代码当然是从上往下执行。但为了提高性能,编译器、JVM 和 CPU 都可能在保证单线程语义不变的前提下,对指令进行重排序。

例如下面这段代码:

int a = 1; int b = 2; a = a + 3; b = b + 4;

如果只看单线程结果,最后 a 是 4,b 是 6。但实际执行时,CPU 可能先计算 b = b + 4,再计算 a = a + 3,甚至把后面的赋值提前。因为在单个线程看来,这种重排并不影响最终结果。

然而在多线程环境下,重排序就可能引发严重问题。一个线程看到另一个线程的执行顺序和代码书写顺序不一致时,就可能出现非常隐蔽的 bug。volatile 的一个重要能力,就是通过内存屏障禁止特定类型的指令重排序。

3. Java 内存模型:volatile 的理论基础

前面反复提到可见性、原子性、有序性,这些背后有一套统一的规范,就是 Java 内存模型,简称 JMM。JMM 不是真实存在的物理硬件,而是一套抽象的规则。它规定了多线程环境下,共享变量的读写顺序、可见性以及哪些重排序是允许的、哪些是不允许的。

理解 JMM 不需要陷入枯燥的规范条文,抓住几个核心点就足够了。

3.1 主内存和工作内存

JMM 把内存抽象成两部分:主内存和工作内存。主内存是共享的,所有线程都能访问;工作内存是线程私有的,每个线程独有一份。

线程不能直接读写主内存中的变量,必须先把变量从主内存拷贝到自己的工作内存,在工作内存中修改后,再把结果写回主内存。这个抽象和真实的 CPU 缓存、寄存器并不完全等同,但足够帮助我们理解并发问题的来源。

可以这样记忆:主内存相当于大家都能看到的共享白板,工作内存相当于每个人手里的草稿纸。你修改自己草稿纸上的数字,并不代表白板上的数字马上变掉。只有当你主动把草稿纸上的结果抄回白板,别人才有机会看到。

3.2 happens-before:判断可见性的“因果关系”

JMM 提出了 happens-before 原则,用来描述两个操作之间的偏序关系。如果操作 A happens-before 操作 B,那么 A 产生的影响对 B 可见,且 A 在时间顺序上排在 B 前面。

可以把 happens-before 理解成一种“因果承诺”。如果 A happens-before B,那么 B 一定能够看到 A 的结果。JMM 保证了几条重要的 happens-before 规则:

  • 程序顺序规则:在同一个线程内,前面的操作 happens-before 后面的操作。
  • 监视器锁规则:对一个锁的解锁 happens-before 后续对这个锁的加锁。
  • volatile 变量规则:对一个 volatile 变量的写 happens-before 后续对这个 volatile 变量的读。
  • 传递性规则:如果 A happens-before B,且 B happens-before C,那么 A happens-before C。

volatile 之所以能解决可见性问题,正是因为它建立在 happens-before 规则之上。一旦某个线程写入了 volatile 变量,后续任何线程读取这个 volatile 变量,都能看到这次写入,并且还能看到写入线程在此之前对其他变量的修改。这个特性我们后续会用代码验证。

4. volatile 到底能保证什么

在进入具体场景之前,我们先给 volatile 的能力做一个清晰、不夸大的总结。很多人背面试题时只记住一句“volatile 保证可见性、禁止指令重排、不保证原子性”,这句话本身没错,但如果不懂其中的细节,还是会在实际开发中踩坑。

4.1 保证可见性

当一个线程修改 volatile 变量后,新值会立刻刷新到主内存。其他线程读取 volatile 变量时,必须从主内存中重新读取,而不是使用自己工作内存里的旧副本。这样,volatile 变量的最新值对所有线程真正可见。

在 StopFlagDemo 中,stop 声明为 volatile 后,主线程把 stop 改成 true 的操作对其他线程可见,worker 线程随即退出循环。

4.2 禁止指令重排序

volatile 的写操作和读操作之间会插入内存屏障,阻止 JVM 和 CPU 对这些 volatile 操作的某些重排序。这样能在一定程度上保证代码执行的有序性。

需要区分清楚的是:volatile 并不能禁止所有重排序。它禁止的是和 volatile 变量相关的那些关键重排序。例如,volatile 写之前的普通写不能被重排到 volatile 写之后;volatile 读之后的操作也不能被重排到 volatile 读之前。这种限制在许多并发设计里非常关键,比如双重检查锁单例模式。

4.3 不保证原子性

volatile 最常被误解的一点,就是以为它能保证原子性。事实是:volatile 对复合操作无能为力。i++、i--、check-then-act 这类“读、改、写”的组合操作,即便变量被 volatile 修饰,也不会因为可见性而变得线程安全。

我们会在后文用计数器的例子直观展示这一点。简单说,如果你需要的只是“一个线程写、多个线程读”,volatile 通常够了;如果你需要“多个线程同时改”,volatile 单独使用远远不够,还得借助锁或者原子类。

5. 深入理解可见性:volatile 的读写语义

光说“保证可见性”还不够,我们来看看 volatile 在读写时到底发生了什么。

5.1 volatile 写:立刻刷新到主内存

当一个线程对一个 volatile 变量执行写操作时,JMM 会把这个线程工作内存中对应的值立即刷新到主内存。这就像我们把草稿纸上的数字第一时间抄回白板,不让它继续藏在草稿纸上。

更重要的是,volatile 写还会把该线程在此之前对其他普通变量的修改一并刷新到主内存。换句话说,volatile 写不仅保证自己可见,还会“顺带”保证写之前的所有修改可见。这个特性在实现“状态发布”时非常有用。

5.2 volatile 读:强制从主内存读

当一个线程读取 volatile 变量时,JMM 会把这个线程工作内存中对应的副本置为无效,强制线程重新从主内存中读取。也就是说,线程不会使用自己草稿纸上的旧数据,而是去共享白板上看最新结果。

同样地,volatile 读之后,这个线程对其他普通变量的后续读取,也都能够看到最新值。volatile 读相当于一个“刷新点”,把线程的工作内存和主内存重新对齐。

5.3 volatile 写读之间的 happens-before 关系

假设线程 A 先写 volatile 变量 v,然后线程 B 再读 v。根据 volatile 变量规则,线程 A 对 v 的写 happens-before 线程 B 对 v 的读。再结合传递性,线程 A 在写 v 之前进行的所有修改,对线程 B 在读 v 之后的所有操作都是可见的。

这个特性可以表达成一句非常实用的话:volatile 变量的写读,可以充当线程之间发布共享状态的“桥梁”。只要 B 读到了 A 写的 volatile 值,B 就能看到 A 在写这个值之前已经完成的所有准备工作。

6. 深入理解有序性:volatile 与内存屏障

指令重排序是 CPU 和编译器提高性能的重要手段,但也是并发编程的隐形杀手。volatile 通过内存屏障来约束重排序。要理解 volatile 的有序性保证,最好先看看 Java 编译器在遇到 volatile 读写时会插入什么样的屏障。

6.1 什么是内存屏障

内存屏障,也常被翻译为内存栅栏,是一种 CPU 指令。它的作用是保证屏障前后的指令不会跨过屏障乱序执行,同时确保某些内存操作对其他 CPU 可见。

可以把内存屏障想象成地铁站里的闸机。闸机可以规定某些人不能越过某条线,或者必须按顺序通过。内存屏障就是 CPU 指令流里的“闸机”,它限制指令重排的范围,并确保内存写入的可见顺序。

6.2 常见的四类屏障

在 JMM 的抽象层面,内存屏障大致分为四类:

  • LoadLoad 屏障:屏障前的读操作完成之后,才执行屏障后的读操作。
  • StoreStore 屏障:屏障前的写操作完成之后,才执行屏障后的写操作。
  • LoadStore 屏障:屏障前的读操作完成之后,才执行屏障后的写操作。
  • StoreLoad 屏障:屏障前的写操作完成之后,才执行屏障后的读操作。

其中 StoreLoad 屏障是成本最高的一种,因为它需要保证写操作完成以后,后续的读操作才能开始。volatile 的写通常会伴随 StoreLoad 屏障,这也是 volatile 写操作相对开销较大的原因之一。

6.3 volatile 写前后的屏障规则

当编译器遇到 volatile 写时,会在它的前后做文章。简单理解,volatile 写之前会插入 StoreStore 屏障,确保在 volatile 写之前的所有普通写都已经刷新到主内存,不会跑到 volatile 写之后。volatile 写之后会插入 StoreLoad 屏障,确保这个写完成之后,后续对其他变量的读操作不会被提前到写之前。

这样做的效果是:在 volatile 写之前的普通操作,不会因为重排序而发生在 volatile 写之后。

6.4 volatile 读前后的屏障规则

当编译器遇到 volatile 读时,会在它的两侧插入 LoadLoad 和 LoadStore 屏障。屏障前的读操作先执行,屏障后的读写操作后执行。这样可以保证 volatile 读之后的普通操作,不会被编译器或 CPU 提前到 volatile 读之前执行。

以上规则共同构成了 volatile 的有序性保证。它们是理解双重检查锁单例为什么需要 volatile 的重要基础。

7. volatile 为什么不保证原子性:用计数器实验证明

前面反复说 volatile 不保证原子性,现在我们用一段实验代码把这一点坐实。假设我们有一个计数器 count,声明为 volatile,然后启动 10 个线程,每个线程对 count 自增 1000 次,理论上最终结果应该是 10000。

public class VolatileCounter { private static volatile int count = 0; public static void main(String[] args) throws InterruptedException { Thread[] threads = new Thread[10]; for (int i = 0; i < threads.length; i++) { threads[i] = new Thread(() -> { for (int j = 0; j < 1000; j++) { count++; } }); threads[i].start(); } for (Thread thread : threads) { thread.join(); } System.out.println("最终 count = " + count); } }

如果你多次运行这段代码,大概率会发现最终结果小于 10000,比如 9847、9912,每次结果都不一样。这说明虽然 count 是 volatile 的,但 count++ 这个操作仍然不是线程安全的。

原因在于 count++ 不是一个原子操作。它分为“读取当前值、计算新值、写回新值”三个步骤。线程 A 和线程 B 可能同时读取到相同的旧值,然后各自写回相同的新值,导致一次自增被覆盖。volatile 只能保证每次读写拿到的都是主内存的最新值,却无法阻止两个线程在“读取”和“写回”之间发生交错。

要解决这个问题,有三种常见方案:

  • 使用 synchronized 或 Lock,把自增操作变成临界区。
  • 使用 AtomicInteger 等原子类,通过 CAS 保证自增的原子性。
  • 如果业务允许,也可以使用 LongAdder,在高并发计数场景下性能更好。

这些方案并不是 volatile 的替代品,而是和 volatile 互补。记住这样一句话就比较稳妥:volatile 负责可见和有序,锁和原子类负责原子。

8. volatile 与 synchronized 的区别

volatile 和 synchronized 经常被放在一起比较。两者都能在一定程度上解决并发问题,但机制和适用场景差异很大。搞懂它们的区别,才能在不同场景下做出正确选择。

8.1 底层机制不同:内存可见性 vs 互斥加锁

volatile 本质上是 Java 内存模型提供的一种可见性保证机制。它通过在读写操作前后插入内存屏障,保证变量最新值及时回到主内存,并限制相关指令重排序。volatile 并不会阻塞线程,也不会让线程进入阻塞队列。

synchronized 则是基于对象监视器实现的互斥锁。线程进入同步块时需要获取锁,获取不到就阻塞等待;退出同步块时释放锁。锁不仅能保证临界区内共享变量的可见性和有序性,还能保证互斥访问,从而保证原子性。

可以把 volatile 理解为“给变量加了一个可见性标签”,而 synchronized 是“给一段代码加了一把锁”。一个解决的是数据是否最新,另一个解决的是操作是否互斥。

8.2 原子性不同:volatile 无原子性,synchronized 有原子性

volatile 修饰的变量在执行 i++、i--、check-then-act 这类复合操作时,仍然不是线程安全的。synchronized 则不同,只要把复合操作放在同一个同步块内,就能保证这些操作作为一个整体被串行执行,不会出现中间状态被其他线程看到。

所以当你只需要保证一个变量的可见性时,使用 volatile 更轻量;当你需要保证一组操作的原子性时,应该使用 synchronized 或 java.util.concurrent 包下的 lock 和原子类。

8.3 性能与开销不同:volatile 更轻,但不是银弹

volatile 的开销主要来自内存屏障,尤其是 StoreLoad 屏障成本较高。但整体而言,volatile 不会引起线程上下文切换和锁竞争,在“一写多读”场景下通常比 synchronized 更轻。

不过这不意味着 volatile 永远比 synchronized 快。现代 JVM 对 synchronized 做了大量优化,比如偏向锁、轻量级锁和锁粗化,在竞争不激烈的情况下,synchronized 的性能也非常可观。选型时应该先看语义是否满足,再看性能差异。

8.4 一张表看懂核心区别

对比维度volatilesynchronized
保证可见性是是
保证原子性否是
禁止重排序部分禁止同步块内保证有序
阻塞线程不会锁竞争时会阻塞
适用场景状态标志、安全发布复合操作、临界区

这张表适合作为面试速记,但真正的判断标准始终是:你的业务逻辑到底需要“可见”还是需要“互斥”。

9. volatile 的典型使用场景

volatile 不是万能的,但它在几个典型场景下非常合适。判断标准可以浓缩成一句话:如果一个变量是“一个线程写、多个线程读”,并且每次写都不依赖当前值,那么 volatile 通常是合适的候选方案。

9.1 状态标志位

这是 volatile 最经典、最不容易用错的场景。用一个 volatile boolean 表示某个任务的停止标志,工作线程不断读取这个标志,控制是否继续运行。我们开篇的 StopFlagDemo 就是这种用法。

private static volatile boolean running = true; public static void stop() { running = false; } public static void main(String[] args) { new Thread(() -> { while (running) { // 执行任务 } System.out.println("任务线程已停止"); }).start(); }

这里 running 只有一个线程写、多个线程读,写操作不依赖当前值,所以 volatile 完全够用,而且比加锁更简单。

9.2 一次性安全发布对象

有些对象在初始化完成后就不会再修改其引用,例如配置对象、缓存初始化对象。此时可以使用 volatile 保证对象引用的可见性,避免其他线程读到未初始化完成的对象。

public class ConfigHolder { private volatile Config config; public Config getConfig() { Config result = config; if (result == null) { result = loadConfig(); config = result; } return result; } private Config loadConfig() { return new Config(); } }

需要提醒的是,这个简单写法只是为了保证引用可见,并不能保证线程安全地只加载一次。如果要求严格单次初始化,应该结合双重检查锁、静态内部类或枚举实现。

9.3 独立状态变量

当多个状态字段相互独立、各自只有一个写线程时,可以把它们都声明为 volatile,借用 happen-before 关系发布最新状态。例如一个简单的统计状态:

public class SystemStatus { private volatile boolean healthy = true; private volatile long version = 0; public void markUnhealthy() { healthy = false; } public void markHealthy() { healthy = true; } public void upgrade() { version++; } public boolean isHealthy() { return healthy; } public long getVersion() { return version; } }

每个变量都有自己的写线程或简单的写语义,互相之间没有复合依赖,volatile 就能很好地承载这种“读多写少”的状态发布需求。

9.4 双重检查锁单例中的关键一环

双重检查锁单例是 volatile 最著名的应用之一,也是很多面试官喜欢追问的场景。下一章我们专门展开。

10. 经典案例:双重检查锁单例模式

单例模式看似简单,但在多线程环境下要实现“懒加载 + 高性能 + 线程安全”并不容易。双重检查锁,即 double-checked locking,就是在尽量减小加锁范围的前提下实现线程安全的懒汉单例。

10.1 如果不加 volatile 会怎样

先看一个容易出问题的版本:

public class Singleton { private static Singleton instance; private Singleton() { } public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }

表面上看,这个代码做了两次 null 判断,避免了每次获取实例都加锁。但问题出在 instance = new Singleton() 这一行。它并不是一个原子操作,在 JVM 层面至少可以拆成三步:给对象分配内存、执行构造方法初始化对象、把内存地址赋给 instance 引用。为了提高性能,编译器或 CPU 可能把后两步重排序:先把内存地址赋给 instance,再执行构造初始化。

如果另一个线程在外面第一次检查 instance 时看到非 null,就会直接返回一个还没有初始化完成的对象,导致拿到一个“残缺”的单例。更糟的是,这种 bug 非常隐蔽,可能在高并发或不同平台下才偶现。

10.2 加 volatile 后为什么安全

把 instance 声明为 volatile 后,volatile 会禁止把对象构造和引用赋值之间的关键步骤重排序,从而保证 instance 引用被赋到变量上时,对象已经完成初始化。修正后的代码如下:

public class Singleton { private static volatile Singleton instance; private Singleton() { } public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }

这里的 volatile 不是为了解决可见性,因为 synchronized 已经能处理可见性问题,而是为了禁止指令重排序。正因为 volatile 写之前的所有普通写不能被重排到 volatile 写之后,对象内部的初始化操作才能保证发生在 instance 引用被发布之前。

如果你不想使用 volatile,也可以选择静态内部类方式,利用 JVM 的类初始化机制天然保证线程安全:

public class Singleton { private Singleton() { } private static class Holder { private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return Holder.INSTANCE; } }

两种写法各有优劣。双重检查锁加 volatile 能实现懒加载,静态内部类写法更简洁且同样懒加载,读者可以根据团队习惯选择。

11. volatile 的常见误区

volatile 被误解的频率很高。下面梳理几个最常见的误区,帮助你避开那些隐蔽的坑。

11.1 误区一:volatile 保证原子性

这是最经典的误解。volatile 只能保证单个读或单个写的可见性,不能让 i++、i-- 这类复合操作变成原子操作。只要操作存在“读旧值、计算新值、写回新值”的序列,就可能出现更新丢失。

11.2 误区二:volatile 能替代所有锁

volatile 只是轻量级同步工具,不具备互斥能力。凡是要保证多个操作串行执行、参与条件判断、修改一组关联状态的场景,仍然需要 synchronized 或显式锁。把 synchronized 全部换成 volatile,很可能把程序从“性能问题”变成“正确性问题”。

11.3 误区三:加上 volatile 就一定能立刻看到最新值

volatile 保证的是:当某个线程写入新值后,后续读取该 volatile 变量的线程能看到这个最新值。它并不代表“所有线程在所有时刻看到的都是同一个值”。并发写、读取时序交错时,不同线程仍然可能先后看到不同状态。理解 happen-before 关系,比简单背诵“立刻可见”更准确。

11.4 误区四:volatile 引用能保证引用对象的内部字段可见

如果声明了一个 volatile 的对象引用,保证的只是这个引用的可见性,而不是对象内部字段的可见性。例如 volatile 修饰一个数组引用,数组元素的修改并不受 volatile 保护;volatile 修饰一个对象,对象内部 int 字段也不自动具备 volatile 语义。如果既要发布引用、又要保护内部状态,通常需要更完整的同步策略。

12. 总结:把 volatile 用得又稳又准

到这里,我们已经从底层机制到实战场景完整梳理了 volatile。最后再把核心结论串起来。

12.1 三句话记住 volatile

  • volatile 保证可见性:写后刷新主内存,读时强制重新读取。
  • volatile 禁止部分重排序:通过内存屏障约束关键读写顺序。
  • volatile 不保证原子性:不能用于 i++ 等复合操作。

12.2 什么时候用 volatile

  • 状态标志位:一个线程写,多个线程读。
  • 一次性发布对象引用:例如双重检查锁中的 instance。
  • 读取频繁、写入简单且不依赖旧值的变量。

12.3 什么时候不要用 volatile

  • 需要复合操作的原子性时。
  • 需要多个线程同时修改变量时。
  • 对象内部状态也需要可见性保障时。
  • 不确定用不用的时候,优先选择更稳的 synchronized 或并发工具。

12.4 后续学习建议

建议依次深入三个方向:一是系统学习 Java 内存模型和 happens-before 规则;二是阅读 AtomicInteger、LongAdder 等原子类的底层 CAS 实现;三是结合 ConcurrentHashMap 的源码,观察 volatile 和锁在实际工程中如何协同工作。理解这些之后,你不仅能回答 volatile 相关面试题,更能在真实并发场景里做出可靠的技术选型。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询