标签:必须 设置 获得 栅栏 long 汇编代码 相互 虚拟 静态
一、什么是volatile关键字
Java语言提供了一种稍弱的同步机制,即volatile变量,用来确保将变量的更新操作通知到其他线程。
当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。
volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。
一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,
那么就具备了两层语义: 1、保证多线程下的可见性 2、禁止进行指令重排序(即保证有序性)。
二、内存模型的相关概念
(1)不同的平台,内存模型是不一样的,但是jvm的内存模型规范是统一的。
(2)线程安全无非是要控制多个线程对某个资源的有序访问或修改。
总结:java的内存模型多线程下,要解决两个主要的问题:可见性和有序性。
(3)我们都知道计算机有高速缓存的存在,处理器并不是每次处理数据都是取内存的。
JVM定义了自己的内存模型,屏蔽了底层平台内存管理细节。
三、并发编程中的三个概念
(1)原子性:
只有简单的读取、赋值(而且必须是将数字赋值给某个变量,变量之间的相互赋值不是原子操作)才是原子操作。
(2)可见性:
多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
(3)有序性:
在Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。
五、使用volatile关键字的场景
(1)使用valatile前后对比
(2)valotile使用前提条件
1、对变量的写操作不依赖于当前值
2、该变量没有包含在具有其他变量的不变式中
简单来说:就是保证该操作是原子性操作
六、volatile的原理和实现机制
摘自《深入理解Java虚拟机》:
“观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现,加入volatile关键字时,会多出一个lock前缀指令”
lock前缀指令实际上相当于一个内存屏障(也成内存栅栏),内存屏障会提供3个功能:
(1)它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;
即在执行到内存屏障这句指令时,在它前面的操作已经全部完成。
(2)它会强制将对缓存的修改操作立即写入主存。
(3)如果是写操作,它会导致其他CPU中对应的缓存行无效。
七、synchronize与volatile的区别
八、java中能创建volatile数组吗?
能,java中可以创建volatile数组,不过只是一个指向数组的引用,
而不是整个数组,如果改变引用指向的数组,将会受到volatile的保护,
但是如果多个线程同时改变数组的元素,volatile 标示符就不能起到之前的保护作用了。
九、volatile能使得一个非原子操作变成原子操作吗?
一个典型的例子是在类中有一个 long 类型的成员变量。如果你知道该成员变量会被多个线程访问,如计数器、价格等,最好是将其设置为 volatile。
为什么?因为 Java 中读取 long 类型变量不是原子的,需要分成两步,如果一个线程正在修改该 long 变量的值,另一个线程可能只能看到该值的一半(前 32 位)。
但是对一个 volatile 型的 long 或 double 变量的读写是原子。
十、volatile 类型变量提供什么保证?
volatile 变量提供顺序和可见性保证,
例如,JVM 或者 JIT为了获得更好的性能会对语句重排序,但是 volatile 类型变量即使在没有同步块的情况下赋值也不会与其他语句重排序。
volatile 提供 happens-before 的保证,确保一个线程的修改能对其他线程是可见的。
某些情况下,volatile 还能提供原子性,如读 64 位数据类型,像 long 和 double 都不是原子的,但 volatile 类型的 double 和 long 就是原子的。
标签:必须 设置 获得 栅栏 long 汇编代码 相互 虚拟 静态
原文地址:https://www.cnblogs.com/fanhaiping/p/10075703.html