文章22 | 阅读 8029 | 点赞0
在并发编程中,我们会遇到三个概念:原子性、可见性、有序性。
在物理机中,为了提高处理速度,处理器不直接和内存进行通信,而是先将系统内存中的数据读取到处理器内部的高速缓存中,然后再进行操作,如下图所示。
Java 内存模型规定了所有的变量都存储在主内存中,同时每条线程都有自己的工作内存,线程的工作内存中保存了被该线程使用的变量的副本,线程对该变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的数据,不同的线程之间也无法直接访问对方工作内存中的变量,不同线程之间变量值的传递均需要通过主内存来完成,如下图所示。
这里所讲的主内存、工作内存和 Java 堆、栈、方法区等并不是同一个层次的堆内存的划分,如果非要勉强对应起来的话,主内存主要对应于 Java 堆中的对象实例数据部分,而工作内存则对应虚拟机栈中部分区域。
volatile 主要有两个作用:
下面我们来逐条分析其实现原理
Java 内存模型对 volatile 变量定义的特殊规则如下所示(V 表示一个 volatile 变量):
上述三个规则中的前两个保证了 volatile 变量的可见性(第三条规则保证了 volatile 变量的有序性)。
volatile 虽然保证了可见性,但在并发情况下它仍然可能是线程不安全的,因此在不符合以下两条规则的运算场景中,我们仍需要通过加锁(使用 sychronized、java.util.concurrent 中的锁或原子类)来保证原子性:
下面我们来从更底层的角度来看一下 volatile 是如何保证可见性的:如果对声明了 volatile 的变量执行写操作,JVM 会向处理器发送一条 Lock 前缀指令,该指令在多核处理器下会引发两件事情:
下面我们来详细说明一下 Lock 前缀指令引发的两件事情。
volatile 关键字会禁止指令重排,它有两层语义:
上面这两句话可能有些抽象,我们举一个例子,代码如下:
/** * x,y 为非 volatile 变量 * flag 为 volatile 变量 */
x = 1; // 语句一
y = 2; // 语句二
flag = 3; // 语句三
x = 4; // 语句四
y = 5; // 语句五
上述代码在进行指令重排时有如下限制:
代码如下:
public class Singleton {
private volatile static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
这里使用 volatile 关键字的作用是禁止指令重排,上述代码中 instance = new Singleton()
实际上不是原子性操作,可以拆分为以下三个步骤:
memory = allocate(); //1 分配对象的内存空间
initInstance(memory); //2 初始化对象
instance = memory; //3 设置 instance 指向刚分配的内存地址
如果没有 volatile 修饰变量 instance 的话,上述伪代码的顺序就可能变为:
memory = allocate(); //1 分配对象的内存空间
instance = memory; //3 设置 instance 指向刚分配的内存地址(此时对象还未初始化)
initInstance(memory); //2 初始化对象
这样的话返回的可能就是还未初始化的对象,有可能会造成程序运行错误。
而如果加上了 volatile 的话,因为 volatile 具有禁止指令重排的作用,对象就可以正常进行初始化了。
下面代码使用了 volatile 保证可见性的作用
volatile boolean flag = false;
while(!flag){
doSomething();
}
public void setFlag() {
flag = true;
}
下面代码使用了 volatile 禁止指令重排的作用
volatile boolean inited = false;
//线程1:
context = loadContext();
inited = true;
//线程2:
while(!inited ){
sleep()
}
doSomethingwithconfig(context);
最后,强烈推荐大家阅读这篇文章:Java并发编程:volatile关键字解析
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/weixin_41685207/article/details/109409674
内容来源于网络,如有侵权,请联系作者删除!