1、volatile用于保证多线程并发可见性,其原理在于阻止jvm对volatile语句进行重排序,另外会提供内存屏障(memory barrier)简单的说,就是当你写一个 volatile 变量之前,Java 内存模型会插入一个写屏障(write barrier),读一个 volatile 变量之前,会插入一个读屏障(read barrier)。
2、在32位的jvm中,double 和 long 都是64位宽,因此对这两种类型的读是分为两部分的,第一次读取第一个 32 位,然后再读剩下的 32 位,这个过程不是原子的,但 Java 中 volatile 型的 long 或 double 变量的读写是原子的。在64位的jvm中这种非原子性读写可被避免:
作者:码农甲
链接:https://www.zhihu.com/question/38816432/answer/227565395
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
规范中的确没有保证。结论:在目前intel平台的x64 hotspot jvm中,long 、double的访问是原子的。这个问题我查了些资料,并且在x86/x64 hotspot上自行验证了。long 在 32位jre8、jre9 上运行是非原子的,会出现写高低位的问题。在64位的jvm上 double long 事实上的确能保证原子性。可以看这个文章的讨论 Value integrity guarantee for concurrent long writes in 64-bit OpenJDK 7/8。 因为jvm也不需要分两段读取写入嘛。至于严格的保证,目前看到的是JEP188 JMM的最新更新,以及JDK9中包含了 -XX:+AlwaysAtomicAccesses 参数以实现对所有Access的原子性保证(我的测试情况来看,只保证64位jdk的情况,32使用此参数依然无效)。更详细的测试可以看看这篇文章 All Accesses Are Atomic 。文章中对于intel x64虚拟机的测试表明,其对long/double的读取写入均未发现非atmoic的出现。且是否增加AlwaysAtomicAccesses参数并不影响性能。不过文章中的结果在 32位虚拟机使用+AlwaysAtomicAccesses参数时,维持了访问long的原子性,我自己在open-jdk9 181-b5 32bit 测试,发现还是存在非原子访问的情况。double因为一般cpu都有专门的浮点单元,其存取哪怕是在32bit jvm上一般都是原子的。上文的测试结果也可以证明。
3、volatile修饰容器,比如数组的时候保证的是对容器的可见性,而不是对容器中的对象的可见性。在修改容器中的元素时不能保证volatile可见性。