volatile 是什么volatile是 Java 的一个关键字是一种同步机制。volatile的作用是作为指令关键字确保本条指令不会因编译器的优化而省略且要求每次直接读值。当某个变量是共享变量且这个变量是被 volatile 修饰的那么在修改了这个变量的值之后再读取该变量的值时可以保证获取到的是修改后的最新的值而不是过期的值。核心思想保证可见性写操作刷新主内存读操作获取最新值禁止指令重排序通过内存屏障Store/Load Barrier保证执行顺序不保证原子性单变量简单读写安全复合操作需锁或原子类二、Java内存模型JMMJava Memory Model简称JMMJava内存模型注意和JVM内存模型区别本身是一种抽象的概念它描述的是一组规则或规范通过这组规范定义了程序中各个变量包括实例字段静态字段和构成数组对象的元素的访问方式。参考 玩命死磕Java内存模型JMM与Volatile关键字底层原理https://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#whatismmhttps://juejin.cn/post/6844903520760496141?searchId2024061923154431D1C78C4D7EDB8805F02.1 计算机硬件内存架构由于 CPU 的处理速度很快相比之下内存的速度就显得很慢所以为了提高 CPU 的整体运行效率减少空闲时间在 CPU 和内存之间会有 cache 层也就是缓存层的存在。虽然缓存的容量比内存小但是缓存的速度却比内存的速度要快得多。线程间对于共享变量的可见性问题并不是直接由多核引起的而是由多级缓存引起的每个核心在获取数据时都会将数据从内存一层层往上读取同样后续对于数据的修改也是先写入到自己的 L1 缓存中然后等待时机再逐层往下同步直到最终刷回内存。2.1 主内存Main Memory定义主内存是Java内存模型中的一个抽象概念它代表了所有变量都存储的共享内存区域。主内存可以被多个线程同时访问。内容主内存存放了程序中所有的类实例、静态数据等变量。这些变量是线程共享的即多个线程都可以访问和修改主内存中的变量。不管是类的成员变量、还是方法中的局部变量又或者共享的类信息、常量、静态变量等数据包括所有线程创建的实例对象都会被存放在主内存中除开栈上分配的对象。作用主内存是线程间通信的桥梁线程间变量值的传递都需要通过主内存来完成。2.2 工作内存Working Memory定义工作内存是Java内存模型中为每个线程分配的一个私有内存区域用于存储线程私有的数据。每个线程都有一个独立的工作内存。应该包括程序计数器、虚拟机栈以及本地方法栈。内容工作内存中存储了当前方法的所有本地变量信息及主内存中变量的副本线程对变量的所有操作读取、赋值等都必须在工作内存中进行而不能直接读写主内存中的数据。主要存储每个线程的工作内存对其它线程不可见比如T1的工作内存中存储着主内存中拷贝回来的某个共享变量副本这对于T2线程也是不可见的。 就算是两个线程执行的是同一段代码、同一个方法它们也只会在各自的工作内存中创建属于当前线程的本地变量、字节码行号指示器、相关Native方法等信息而不会两者之间共用一块内存的数据。 注意由于工作内存是每个线程的私有数据线程间无法相互访问工作内存线程之间的通讯需要依赖于主存因此存储在工作内存的数据不存在线程安全问题。2.3 工作内存与主内存的关系JMM 有以下规定1所有的变量都存储在主内存中同时每个线程拥有自己独立的工作内存而工作内存中的变量的内容是主内存中该变量的拷贝2线程不能直接读 / 写主内存中的变量但可以操作自己工作内存中的变量然后再同步到主内存中这样其他线程就可以看到本次修改3 主内存是由多个线程所共享的但线程间不共享各自的工作内存如果线程间需要通信则必须借助主内存中转来完成。三、volitale作用面试官没想到一个Volatile我都能跟他扯半小时彻底理解volatile3.1 volitale主要作用保证内存线程可见保证了不同线程对这个变量进行操作时的可见性即一个线程修改了某个变量的值这新值对其他线程来说是立即可见的。Happens-before 关系中对于 volatile 是这样描述的对一个 volatile 变量的写操作 happen-before 后面对该变量的读操作。这就代表了如果变量被 volatile 修饰那么每次修改之后接下来在读取这个变量的时候一定能读取到该变量最新的值。保证内存有序性防止指令重排序先介绍一下 as-if-serial语义不管怎么重排序单线程程序的执行结果不会改变。在满足 as-if-serial 语义的前提下由于编译器或 CPU 的优化代码的实际执行顺序可能与我们编写的顺序是不同的这在单线程的情况下是没问题的但是一旦引入多线程这种乱序就可能会导致严重的线程安全问题。用了 volatile 关键字就可以在一定程度上禁止这种重排序。3.2 volatile解决内存可见性怎么做volatile修饰共享变量什么原理每个线程操作数据的时候会把数据从主内存读取到自己的工作内存如果他操作了数据并且写会了他其他已经读取的线程的变量副本就会失效了需要都数据进行操作又要再次去主内存中读取了。volatile保证不同线程对共享变量操作的可见性也就是说一个线程修改了volatile修饰的变量当修改写回主内存时另外一个线程立即看到最新的值。3.3 volatile解决内存有序性什么是重排序为了提高性能编译器和处理器常常会对既定的代码执行顺序进行指令重排序as-if-serial语义不管怎么重排序单线程程序的执行结果不会改变。在满足 as-if-serial 语义的前提下由于编译器或 CPU 的优化代码的实际执行顺序可能与我们编写的顺序是不同的这在单线程的情况下是没问题的但是一旦引入多线程这种乱序就可能会导致严重的线程安全问题。原理是什么——内存屏障volitale使用了内存屏障保证不会执行重排序。JMM内存屏障volatile重排序规则表java编译器会在生成指令系列时在适当的位置会插入内存屏障指令来禁止特定类型的处理器重排序。为了实现volatile的内存语义JMM会限制特定类型的编译器和处理器重排序JMM会针对编译器制定volatile重排序规则表NO表示禁止重排序。为了实现volatile内存语义时编译器在生成字节码时会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。对于编译器来说发现一个最优布置来最小化插入屏障的总数几乎是不可能的为此JMM采取了保守策略在每个volatile写操作的前面插入一个StoreStore屏障在每个volatile写操作的后面插入一个StoreLoad屏障在每个volatile读操作的后面插入一个LoadLoad屏障在每个volatile读操作的后面插入一个LoadStore屏障。需要注意的是volatile写是在前面和后面分别插入内存屏障而volatile读操作是在后面插入两个内存屏障StoreStore屏障禁止上面的普通写和下面的volatile写重排序StoreLoad屏障防止上面的volatile写与下面可能有的volatile读/写重排序LoadLoad屏障禁止下面所有的普通读操作和上面的volatile读重排序LoadStore屏障禁止下面所有的普通写操作和上面的volatile读重排序四、happens-before规则如果一个操作执行的结果需要对另一个操作可见那么这两个操作之间必须存在happens-before关系。volatile域规则对一个volatile域的写操作happens-before于任意线程后续对这个volatile域的读。如果现在我的变了falg变成了false那么后面的那个操作一定要知道我变了。聊了这么多我们要知道Volatile是没办法保证原子性的一定要保证原子性可以使用其他方法。五、volitale和synchronized对比相比于 synchronized 或者 Lockvolatile 是更轻量的因为使用 volatile 不会发生上下文切换等开销很大的情况不会让线程阻塞。但正是由于它的开销相对比较小所以它的效果也就是能力相对也小一些。相似性volatile 可以看作是一个轻量版的 synchronized比如一个共享变量如果自始至终只被各个线程赋值和读取而没有其他操作的话那么就可以用 volatile 来代替 synchronized 或者代替原子变量足以保证线程安全。实际上对 volatile 字段的每次读取或写入都类似于“半同步”——读取 volatile 与获取 synchronized 锁有相同的内存语义而写入 volatile 与释放 synchronized 锁具有相同的语义。不可代替但是在更多的情况下volatile 是不能代替 synchronized 的volatile 并没有提供原子性和互斥性。性能方面volatile 属性的读写操作都是无锁的正是因为无锁所以不需要花费时间在获取锁和释放锁上所以说它是高性能的比 synchronized 性能更好
volatile关键字最全总结:原理剖析与实例讲解
发布时间:2026/6/1 0:33:32
volatile 是什么volatile是 Java 的一个关键字是一种同步机制。volatile的作用是作为指令关键字确保本条指令不会因编译器的优化而省略且要求每次直接读值。当某个变量是共享变量且这个变量是被 volatile 修饰的那么在修改了这个变量的值之后再读取该变量的值时可以保证获取到的是修改后的最新的值而不是过期的值。核心思想保证可见性写操作刷新主内存读操作获取最新值禁止指令重排序通过内存屏障Store/Load Barrier保证执行顺序不保证原子性单变量简单读写安全复合操作需锁或原子类二、Java内存模型JMMJava Memory Model简称JMMJava内存模型注意和JVM内存模型区别本身是一种抽象的概念它描述的是一组规则或规范通过这组规范定义了程序中各个变量包括实例字段静态字段和构成数组对象的元素的访问方式。参考 玩命死磕Java内存模型JMM与Volatile关键字底层原理https://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#whatismmhttps://juejin.cn/post/6844903520760496141?searchId2024061923154431D1C78C4D7EDB8805F02.1 计算机硬件内存架构由于 CPU 的处理速度很快相比之下内存的速度就显得很慢所以为了提高 CPU 的整体运行效率减少空闲时间在 CPU 和内存之间会有 cache 层也就是缓存层的存在。虽然缓存的容量比内存小但是缓存的速度却比内存的速度要快得多。线程间对于共享变量的可见性问题并不是直接由多核引起的而是由多级缓存引起的每个核心在获取数据时都会将数据从内存一层层往上读取同样后续对于数据的修改也是先写入到自己的 L1 缓存中然后等待时机再逐层往下同步直到最终刷回内存。2.1 主内存Main Memory定义主内存是Java内存模型中的一个抽象概念它代表了所有变量都存储的共享内存区域。主内存可以被多个线程同时访问。内容主内存存放了程序中所有的类实例、静态数据等变量。这些变量是线程共享的即多个线程都可以访问和修改主内存中的变量。不管是类的成员变量、还是方法中的局部变量又或者共享的类信息、常量、静态变量等数据包括所有线程创建的实例对象都会被存放在主内存中除开栈上分配的对象。作用主内存是线程间通信的桥梁线程间变量值的传递都需要通过主内存来完成。2.2 工作内存Working Memory定义工作内存是Java内存模型中为每个线程分配的一个私有内存区域用于存储线程私有的数据。每个线程都有一个独立的工作内存。应该包括程序计数器、虚拟机栈以及本地方法栈。内容工作内存中存储了当前方法的所有本地变量信息及主内存中变量的副本线程对变量的所有操作读取、赋值等都必须在工作内存中进行而不能直接读写主内存中的数据。主要存储每个线程的工作内存对其它线程不可见比如T1的工作内存中存储着主内存中拷贝回来的某个共享变量副本这对于T2线程也是不可见的。 就算是两个线程执行的是同一段代码、同一个方法它们也只会在各自的工作内存中创建属于当前线程的本地变量、字节码行号指示器、相关Native方法等信息而不会两者之间共用一块内存的数据。 注意由于工作内存是每个线程的私有数据线程间无法相互访问工作内存线程之间的通讯需要依赖于主存因此存储在工作内存的数据不存在线程安全问题。2.3 工作内存与主内存的关系JMM 有以下规定1所有的变量都存储在主内存中同时每个线程拥有自己独立的工作内存而工作内存中的变量的内容是主内存中该变量的拷贝2线程不能直接读 / 写主内存中的变量但可以操作自己工作内存中的变量然后再同步到主内存中这样其他线程就可以看到本次修改3 主内存是由多个线程所共享的但线程间不共享各自的工作内存如果线程间需要通信则必须借助主内存中转来完成。三、volitale作用面试官没想到一个Volatile我都能跟他扯半小时彻底理解volatile3.1 volitale主要作用保证内存线程可见保证了不同线程对这个变量进行操作时的可见性即一个线程修改了某个变量的值这新值对其他线程来说是立即可见的。Happens-before 关系中对于 volatile 是这样描述的对一个 volatile 变量的写操作 happen-before 后面对该变量的读操作。这就代表了如果变量被 volatile 修饰那么每次修改之后接下来在读取这个变量的时候一定能读取到该变量最新的值。保证内存有序性防止指令重排序先介绍一下 as-if-serial语义不管怎么重排序单线程程序的执行结果不会改变。在满足 as-if-serial 语义的前提下由于编译器或 CPU 的优化代码的实际执行顺序可能与我们编写的顺序是不同的这在单线程的情况下是没问题的但是一旦引入多线程这种乱序就可能会导致严重的线程安全问题。用了 volatile 关键字就可以在一定程度上禁止这种重排序。3.2 volatile解决内存可见性怎么做volatile修饰共享变量什么原理每个线程操作数据的时候会把数据从主内存读取到自己的工作内存如果他操作了数据并且写会了他其他已经读取的线程的变量副本就会失效了需要都数据进行操作又要再次去主内存中读取了。volatile保证不同线程对共享变量操作的可见性也就是说一个线程修改了volatile修饰的变量当修改写回主内存时另外一个线程立即看到最新的值。3.3 volatile解决内存有序性什么是重排序为了提高性能编译器和处理器常常会对既定的代码执行顺序进行指令重排序as-if-serial语义不管怎么重排序单线程程序的执行结果不会改变。在满足 as-if-serial 语义的前提下由于编译器或 CPU 的优化代码的实际执行顺序可能与我们编写的顺序是不同的这在单线程的情况下是没问题的但是一旦引入多线程这种乱序就可能会导致严重的线程安全问题。原理是什么——内存屏障volitale使用了内存屏障保证不会执行重排序。JMM内存屏障volatile重排序规则表java编译器会在生成指令系列时在适当的位置会插入内存屏障指令来禁止特定类型的处理器重排序。为了实现volatile的内存语义JMM会限制特定类型的编译器和处理器重排序JMM会针对编译器制定volatile重排序规则表NO表示禁止重排序。为了实现volatile内存语义时编译器在生成字节码时会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。对于编译器来说发现一个最优布置来最小化插入屏障的总数几乎是不可能的为此JMM采取了保守策略在每个volatile写操作的前面插入一个StoreStore屏障在每个volatile写操作的后面插入一个StoreLoad屏障在每个volatile读操作的后面插入一个LoadLoad屏障在每个volatile读操作的后面插入一个LoadStore屏障。需要注意的是volatile写是在前面和后面分别插入内存屏障而volatile读操作是在后面插入两个内存屏障StoreStore屏障禁止上面的普通写和下面的volatile写重排序StoreLoad屏障防止上面的volatile写与下面可能有的volatile读/写重排序LoadLoad屏障禁止下面所有的普通读操作和上面的volatile读重排序LoadStore屏障禁止下面所有的普通写操作和上面的volatile读重排序四、happens-before规则如果一个操作执行的结果需要对另一个操作可见那么这两个操作之间必须存在happens-before关系。volatile域规则对一个volatile域的写操作happens-before于任意线程后续对这个volatile域的读。如果现在我的变了falg变成了false那么后面的那个操作一定要知道我变了。聊了这么多我们要知道Volatile是没办法保证原子性的一定要保证原子性可以使用其他方法。五、volitale和synchronized对比相比于 synchronized 或者 Lockvolatile 是更轻量的因为使用 volatile 不会发生上下文切换等开销很大的情况不会让线程阻塞。但正是由于它的开销相对比较小所以它的效果也就是能力相对也小一些。相似性volatile 可以看作是一个轻量版的 synchronized比如一个共享变量如果自始至终只被各个线程赋值和读取而没有其他操作的话那么就可以用 volatile 来代替 synchronized 或者代替原子变量足以保证线程安全。实际上对 volatile 字段的每次读取或写入都类似于“半同步”——读取 volatile 与获取 synchronized 锁有相同的内存语义而写入 volatile 与释放 synchronized 锁具有相同的语义。不可代替但是在更多的情况下volatile 是不能代替 synchronized 的volatile 并没有提供原子性和互斥性。性能方面volatile 属性的读写操作都是无锁的正是因为无锁所以不需要花费时间在获取锁和释放锁上所以说它是高性能的比 synchronized 性能更好