volatile的原理

正常情况下,操作系统不会进行共享变量的缓存一致性校验,只有当共享变量被volatile关键字修饰了,该变量所在的缓存行才被要求进行缓存一致性的校验。

下面通过volatile关键字的汇编代码,分析一下volatile关键字的底层原理。Java代码如下:

package org.cloudxue.visiable; public class VolatileVar { volatile int var = 0; public void setVar (int var){ System.out.println("setVar = " var); this.var = var; } public static void main(String[] args) { VolatileVar var = new VolatileVar(); var.setVar(20); } }

以上代码通过IDEA编辑,进入到class目录,执行以下命令:

➜ classes git:(main) pwd /Users/xuexiao/Work/QDBank/Idea-WorkSpace/SpringBootMuiltModule/multi_thread_lecture/target/classes ➜ classes git:(main) java -server -Xcomp -XX:-Inline -XX: UnlockDiagnosticVMOptions -XX: PrintAssembly org/cloudxue/visiable/VolatileVar > ~/Downloads/volatile.log Java HotSpot(TM) 64-Bit Server VM warning: PrintAssembly is enabled; turning on DebugNonSafepoints to gain additional output ➜ classes git:(main) 命令选项说明: -Xcomp:表示永远以编译模式运行,禁止解释器模式 -XX:Inline:禁止内联优化 -server:设置虚拟机使用“server”模式。该模式启动比“client”模式慢,但是运行性能高,Linux缺省使用server模式

输出的volatile.log日志文件较大,选取其中的关键部分

0x000000010c7c3c54: mov %r12d,0xc(%rbp) 0x000000010c7c3c58: lock addl $0x0,(%rsp) ;*putfield var ; - org.cloudxue.visiable.VolatileVar::<init>@6 (line 11) 0x000000010c7c3c5d: add $0x10,%rsp 0x000000010c7c3c61: pop %rbp

通过以上汇编指令可以看到,操作var共享变量之前,多出一个lock前缀指令:local addl,该lock前缀指令有三个功能:

  1. 将当前处理器缓存行的数据立即写回系统内存。Lock前缀指令导致在执行期间,CPU可以独占共享内存;
  2. lock前缀指令会引起在其他CPU里缓存了该内存地址的数据无效。写回操作要经过总线传播数据,而每个处理器通过嗅探在总线是哪个传播的数据来检查自己缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置为无效状态,当处理器要对这个值进行修改时,会强制重新从系统内存里把数据读到处理器缓存;
  3. lock前缀指令禁止指令重排序

总之,volatile关键词保障内存可见性的原理,涉及到MESI协议、内存屏障等硬件层面的技术。

volatile语义中的内存屏障

在Java代码中,volatile关键字主要有两层语义:

volatile关键字除了保障内存可见性,还能确保执行的有序性。volatile语义中的有序性,是通过内存屏障指令来确保的。为了实现volatile关键字的有序性,JVM编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。

JMM建议JVM采取保守策略对重排序进行严格禁止,下面是基于保守策略的volatile操作的内存屏障插入策略:

volatile写操作内存屏障

JVM采用保守策略,为volatile写操作添加的内存屏障如下

多线程的五个状态(多线程基础volatile关键字详解)(1)

volatile写操作内存屏障插入策略

StoreStore屏障可以保证

所以,在volatile写入之前,其前面的所有普通写操作已经对任意处理器可见了,并且volatile写操作不会被重排到屏障前面去。

StoreLoad屏障可以保证:

所以,在volatile写完成后,高速缓存已经刷新成最新数据,对后面所有处理器的读操作都可见了。

volatile读操作内存屏障

JVM采用保守策略,为volatile读操作添加的内存屏障如下:

多线程的五个状态(多线程基础volatile关键字详解)(2)

volatile读操作内存屏障插入策略

LoadLoad屏障可以保证:

所以,在volatile读之后,所有高速缓存中的数据都是从主存加载且是最新的;

LoadStore屏障可以保证:

所以,volatile读之后,所有高速缓存中的数据都是从主内存加载且是最新的。

实际上,以上JMM建议的对volatile写和volatile读的内存屏障插入策略是针对任意处理器平台的,是非常保守的,不同处理器有不同“松紧度”的处理器内存模型,只要不改变volatile读写操作的内存语义,不同JVM编译器可以根据实际情况省略不必要的JMM屏障。

volatile变量写入的操作性能提升

对每一个volatile变量的每一次读写,JVM都会插入一系列的内存屏障,这就意味着对一个volatile变量每一次写入操作,性能是较低的。尤其是StoreLoad屏障,为了维护内核缓存的强一致性,此屏障可谓不惜血本。

那么在不是每一次对volatile变量的读写都需要保证其内存可见性,或者说,仅仅是某次特定的volatile读写需要保障可见性,可以使用如下两个策略对volatile进行性能优化:

volatile不具备原子性

使用volatile可以保证数据的可见性,但不能保证数据的原子性,对于volatile修饰的变量进行注入自增的复合操作,仍会存在线程不安全问题。如:

package org.cloudxue.visiable; import org.cloudxue.common.util.Print; import org.cloudxue.common.util.ThreadUtil; import org.junit.Test; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; public class VolatileDemo { private volatile long value; @Test public void testVolatileAtomic() throws InterruptedException{ final int TASK_AMOUNT = 10; ExecutorService pool = ThreadUtil.getCpuIntenseTargetThreadPool(); CountDownLatch latch = new CountDownLatch(TASK_AMOUNT); final int TURN = 10000; long start = System.currentTimeMillis(); for (int i = 0; i < TASK_AMOUNT; i ) { pool.submit(() -> { for(int j = 0; j < TURN; j ) { value ; } latch.countDown(); }); } latch.await(); float time = (System.currentTimeMillis() - start) / 1000F; Print.tcfo("运行时长" time); Print.tcfo("累加结果为: " value); Print.tcfo("与预期差值: " (TASK_AMOUNT* TURN - value)); } } ============= /Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home/bin/java -ea -Didea.test.cyclic.buffer.size=1048576 -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=65474:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8 -classpath /Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/junit/lib/junit5-rt.jar:/Applications/IntelliJ IDEA.app/Contents/plugins/junit/lib/junit-rt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home/jre/lib/deploy.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home/jre/lib/ext/cldrdata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home/jre/lib/ext/jaccess.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home/jre/lib/ext/jfxrt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home/jre/lib/ext/nashorn.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home/jre/lib/javaws.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home/jre/lib/jfxswt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home/jre/lib/management-agent.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home/jre/lib/plugin.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home/lib/ant-javafx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home/lib/dt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home/lib/javafx-mx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home/lib/jconsole.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home/lib/packager.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home/lib/sa-jdi.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home/lib/tools.jar:/Users/xuexiao/Work/QDBank/Idea-WorkSpace/SpringBootMuiltModule/multi_thread_lecture/target/classes:/Users/xuexiao/Work/QDBank/Idea-WorkSpace/SpringBootMuiltModule/democommon/target/classes:/Users/xuexiao/.m2/repository/org/springframework/boot/spring-boot-starter/2.3.4.RELEASE/spring-boot-starter-2.3.4.RELEASE.jar:/Users/xuexiao/.m2/repository/org/springframework/boot/spring-boot/2.3.4.RELEASE/spring-boot-2.3.4.RELEASE.jar:/Users/xuexiao/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.3.4.RELEASE/spring-boot-autoconfigure-2.3.4.RELEASE.jar:/Users/xuexiao/.m2/repository/org/springframework/boot/spring-boot-starter-logging/2.3.4.RELEASE/spring-boot-starter-logging-2.3.4.RELEASE.jar:/Users/xuexiao/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar:/Users/xuexiao/.m2/repository/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar:/Users/xuexiao/.m2/repository/org/apache/logging/log4j/log4j-to-slf4j/2.13.3/log4j-to-slf4j-2.13.3.jar:/Users/xuexiao/.m2/repository/org/apache/logging/log4j/log4j-api/2.13.3/log4j-api-2.13.3.jar:/Users/xuexiao/.m2/repository/org/slf4j/jul-to-slf4j/1.7.30/jul-to-slf4j-1.7.30.jar:/Users/xuexiao/.m2/repository/jakarta/annotation/jakarta.annotation-api/1.3.5/jakarta.annotation-api-1.3.5.jar:/Users/xuexiao/.m2/repository/org/yaml/snakeyaml/1.26/snakeyaml-1.26.jar:/Users/xuexiao/.m2/repository/org/projectlombok/lombok/1.16.10/lombok-1.16.10.jar:/Users/xuexiao/.m2/repository/com/alibaba/easyexcel/3.0.5/easyexcel-3.0.5.jar:/Users/xuexiao/.m2/repository/org/apache/poi/poi/4.1.2/poi-4.1.2.jar:/Users/xuexiao/.m2/repository/commons-codec/commons-codec/1.13/commons-codec-1.13.jar:/Users/xuexiao/.m2/repository/org/apache/commons/commons-collections4/4.4/commons-collections4-4.4.jar:/Users/xuexiao/.m2/repository/org/apache/commons/commons-math3/3.6.1/commons-math3-3.6.1.jar:/Users/xuexiao/.m2/repository/com/zaxxer/SparseBitSet/1.2/SparseBitSet-1.2.jar:/Users/xuexiao/.m2/repository/org/apache/poi/poi-ooxml/4.1.2/poi-ooxml-4.1.2.jar:/Users/xuexiao/.m2/repository/org/apache/commons/commons-compress/1.19/commons-compress-1.19.jar:/Users/xuexiao/.m2/repository/com/github/virtuald/curvesapi/1.06/curvesapi-1.06.jar:/Users/xuexiao/.m2/repository/org/apache/poi/poi-ooxml-schemas/4.1.2/poi-ooxml-schemas-4.1.2.jar:/Users/xuexiao/.m2/repository/org/apache/xmlbeans/xmlbeans/3.1.0/xmlbeans-3.1.0.jar:/Users/xuexiao/.m2/repository/org/apache/commons/commons-csv/1.8/commons-csv-1.8.jar:/Users/xuexiao/.m2/repository/cglib/cglib/3.3.0/cglib-3.3.0.jar:/Users/xuexiao/.m2/repository/org/ow2/asm/asm/7.1/asm-7.1.jar:/Users/xuexiao/.m2/repository/org/slf4j/slf4j-api/1.7.32/slf4j-api-1.7.32.jar:/Users/xuexiao/.m2/repository/org/ehcache/ehcache/3.8.1/ehcache-3.8.1.jar:/Users/xuexiao/.m2/repository/org/glassfish/jaxb/jaxb-runtime/2.3.1/jaxb-runtime-2.3.1.jar:/Users/xuexiao/.m2/repository/javax/xml/bind/jaxb-api/2.3.1/jaxb-api-2.3.1.jar:/Users/xuexiao/.m2/repository/org/glassfish/jaxb/txw2/2.3.1/txw2-2.3.1.jar:/Users/xuexiao/.m2/repository/com/sun/istack/istack-commons-runtime/3.0.7/istack-commons-runtime-3.0.7.jar:/Users/xuexiao/.m2/repository/org/jvnet/staxex/stax-ex/1.8/stax-ex-1.8.jar:/Users/xuexiao/.m2/repository/com/sun/xml/fastinfoset/FastInfoset/1.2.15/FastInfoset-1.2.15.jar:/Users/xuexiao/.m2/repository/javax/activation/javax.activation-api/1.2.0/javax.activation-api-1.2.0.jar:/Users/xuexiao/.m2/repository/com/alibaba/fastjson/1.2.76/fastjson-1.2.76.jar:/Users/xuexiao/.m2/repository/org/openjdk/jol/jol-core/0.11/jol-core-0.11.jar:/Users/xuexiao/.m2/repository/junit/junit/4.11/junit-4.11.jar:/Users/xuexiao/.m2/repository/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar:/Users/xuexiao/.m2/repository/org/springframework/boot/spring-boot-starter-web/2.3.4.RELEASE/spring-boot-starter-web-2.3.4.RELEASE.jar:/Users/xuexiao/.m2/repository/org/springframework/boot/spring-boot-starter-json/2.3.4.RELEASE/spring-boot-starter-json-2.3.4.RELEASE.jar:/Users/xuexiao/.m2/repository/com/fasterxml/jackson/core/jackson-databind/2.11.2/jackson-databind-2.11.2.jar:/Users/xuexiao/.m2/repository/com/fasterxml/jackson/core/jackson-annotations/2.11.2/jackson-annotations-2.11.2.jar:/Users/xuexiao/.m2/repository/com/fasterxml/jackson/core/jackson-core/2.11.2/jackson-core-2.11.2.jar:/Users/xuexiao/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.11.2/jackson-datatype-jdk8-2.11.2.jar:/Users/xuexiao/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.11.2/jackson-datatype-jsr310-2.11.2.jar:/Users/xuexiao/.m2/repository/com/fasterxml/jackson/module/jackson-module-parameter-names/2.11.2/jackson-module-parameter-names-2.11.2.jar:/Users/xuexiao/.m2/repository/org/springframework/boot/spring-boot-starter-tomcat/2.3.4.RELEASE/spring-boot-starter-tomcat-2.3.4.RELEASE.jar:/Users/xuexiao/.m2/repository/org/apache/tomcat/embed/tomcat-embed-core/9.0.38/tomcat-embed-core-9.0.38.jar:/Users/xuexiao/.m2/repository/org/glassfish/jakarta.el/3.0.3/jakarta.el-3.0.3.jar:/Users/xuexiao/.m2/repository/org/apache/tomcat/embed/tomcat-embed-websocket/9.0.38/tomcat-embed-websocket-9.0.38.jar:/Users/xuexiao/.m2/repository/org/springframework/spring-web/5.2.9.RELEASE/spring-web-5.2.9.RELEASE.jar:/Users/xuexiao/.m2/repository/org/springframework/spring-beans/5.2.9.RELEASE/spring-beans-5.2.9.RELEASE.jar:/Users/xuexiao/.m2/repository/org/springframework/spring-webmvc/5.2.9.RELEASE/spring-webmvc-5.2.9.RELEASE.jar:/Users/xuexiao/.m2/repository/org/springframework/spring-aop/5.2.9.RELEASE/spring-aop-5.2.9.RELEASE.jar:/Users/xuexiao/.m2/repository/org/springframework/spring-context/5.2.9.RELEASE/spring-context-5.2.9.RELEASE.jar:/Users/xuexiao/.m2/repository/org/springframework/spring-expression/5.2.9.RELEASE/spring-expression-5.2.9.RELEASE.jar:/Users/xuexiao/.m2/repository/org/springframework/boot/spring-boot-starter-test/2.3.4.RELEASE/spring-boot-starter-test-2.3.4.RELEASE.jar:/Users/xuexiao/.m2/repository/org/springframework/boot/spring-boot-test/2.3.4.RELEASE/spring-boot-test-2.3.4.RELEASE.jar:/Users/xuexiao/.m2/repository/org/springframework/boot/spring-boot-test-autoconfigure/2.3.4.RELEASE/spring-boot-test-autoconfigure-2.3.4.RELEASE.jar:/Users/xuexiao/.m2/repository/com/jayway/jsonpath/json-path/2.4.0/json-path-2.4.0.jar:/Users/xuexiao/.m2/repository/net/minidev/json-smart/2.3/json-smart-2.3.jar:/Users/xuexiao/.m2/repository/net/minidev/accessors-smart/1.2/accessors-smart-1.2.jar:/Users/xuexiao/.m2/repository/jakarta/xml/bind/jakarta.xml.bind-api/2.3.3/jakarta.xml.bind-api-2.3.3.jar:/Users/xuexiao/.m2/repository/jakarta/activation/jakarta.activation-api/1.2.2/jakarta.activation-api-1.2.2.jar:/Users/xuexiao/.m2/repository/org/assertj/assertj-core/3.16.1/assertj-core-3.16.1.jar:/Users/xuexiao/.m2/repository/org/hamcrest/hamcrest/2.2/hamcrest-2.2.jar:/Users/xuexiao/.m2/repository/org/junit/jupiter/junit-jupiter/5.6.2/junit-jupiter-5.6.2.jar:/Users/xuexiao/.m2/repository/org/junit/jupiter/junit-jupiter-api/5.6.2/junit-jupiter-api-5.6.2.jar:/Users/xuexiao/.m2/repository/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar:/Users/xuexiao/.m2/repository/org/junit/platform/junit-platform-commons/1.6.2/junit-platform-commons-1.6.2.jar:/Users/xuexiao/.m2/repository/org/junit/jupiter/junit-jupiter-params/5.6.2/junit-jupiter-params-5.6.2.jar:/Users/xuexiao/.m2/repository/org/junit/jupiter/junit-jupiter-engine/5.6.2/junit-jupiter-engine-5.6.2.jar:/Users/xuexiao/.m2/repository/org/junit/vintage/junit-vintage-engine/5.6.2/junit-vintage-engine-5.6.2.jar:/Users/xuexiao/.m2/repository/org/apiguardian/apiguardian-api/1.1.0/apiguardian-api-1.1.0.jar:/Users/xuexiao/.m2/repository/org/junit/platform/junit-platform-engine/1.6.2/junit-platform-engine-1.6.2.jar:/Users/xuexiao/.m2/repository/org/mockito/mockito-core/3.3.3/mockito-core-3.3.3.jar:/Users/xuexiao/.m2/repository/net/bytebuddy/byte-buddy/1.10.5/byte-buddy-1.10.5.jar:/Users/xuexiao/.m2/repository/net/bytebuddy/byte-buddy-agent/1.10.5/byte-buddy-agent-1.10.5.jar:/Users/xuexiao/.m2/repository/org/objenesis/objenesis/2.6/objenesis-2.6.jar:/Users/xuexiao/.m2/repository/org/mockito/mockito-junit-jupiter/3.3.3/mockito-junit-jupiter-3.3.3.jar:/Users/xuexiao/.m2/repository/org/skyscreamer/jsonassert/1.5.0/jsonassert-1.5.0.jar:/Users/xuexiao/.m2/repository/com/vaadin/external/google/android-json/0.0.20131108.vaadin1/android-json-0.0.20131108.vaadin1.jar:/Users/xuexiao/.m2/repository/org/springframework/spring-core/5.2.9.RELEASE/spring-core-5.2.9.RELEASE.jar:/Users/xuexiao/.m2/repository/org/springframework/spring-jcl/5.2.9.RELEASE/spring-jcl-5.2.9.RELEASE.jar:/Users/xuexiao/.m2/repository/org/springframework/spring-test/5.2.9.RELEASE/spring-test-5.2.9.RELEASE.jar:/Users/xuexiao/.m2/repository/org/xmlunit/xmlunit-core/2.7.0/xmlunit-core-2.7.0.jar com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 org.cloudxue.visiable.VolatileDemo,testVolatileAtomic [main|VolatileDemo.testVolatileAtomic]:运行时长0.064 [main|VolatileDemo.testVolatileAtomic]:累加结果为: 48699 [main|VolatileDemo.testVolatileAtomic]:与预期差值: 51301

为什么volatile变量的复合操作不具备原子性呢。首先回顾一下JMM对变量的读取和写入的流程:

对于普通的非volatile变量而言,JMM要求保持read、load有相对顺序即可。

对于关键字volatile修饰的变量而言,具有两个重要的语义:

  1. volatile修饰的变量在变量值发生改变的时候,会立刻同步到主内存,并使其他线程的变量副本失效;
  2. 禁止指令重排序。硬件层面通过在指令前后加入内存屏障来实现,编译器级别则通过下面的规则来实现:

a、使用volatile修饰的变量,其read、load、use操作都是连续出现的,所以每次使用变量的时候,都从主内存读取最新的变量值,替换私有内存中的变量副本值;

b、对同一变量的assign、store、write操作都是连续出现的,所以每次对变量的改变都会立马同步到主内存中。

虽然volatile修饰的变量可以强制刷新内存,其要求对变量读相关(read、load、use)、写相关(assign、store、write)操作连续出现,但是这些操作可能在不同的CPU内核上执行,还是可能出现脏数据的情况。

例如,上述VolatileDemo的代码,线程A、线程B分别运行在Core1、Core2上,value值为0。线程A、B都将value值读取到自己的工作内存中。现在线程A将value变成1之后,完成了assign、store操作,但是在执行write操作时,CPU的时间片用完了,线程A被闲置,write操作没有达到主存。由于线程A的store指令触发了写的信号,线程B缓存过期,重新从主存中读取value,此时value还是0。当线程B执行完所有的操作后,将value值变为1写入主存。此时线程A又拿到了时间片,重新执行store操作,将过期的1,写入主存。

多线程的五个状态(多线程基础volatile关键字详解)(3)

所以,对于复合操作,volatile变量无法保证原子性。若需保证复合操作的原子性,通常需要使用锁。在高并发场景下volatile变量一定要与Java的显示锁结合使用

,