文章目录
Volatile底层实现
作用 :保证内存可见性
Volatile缓存可见性实现原理:
底层实现主要通过汇编lock前缀指令,他会锁定这块区域的缓存(缓存行锁定),并写回到主存
IA-32架构软件开发者手册对lock指令的解释:
- 会将当前处理器缓存行的数据立即写回到系统内存
- 这个写回内存的操作会引起其它CPU里缓存修改内存地址的数据无效(MESI)
- 提供内存屏障功能,使lock前后指令不能重排序
DCL锁
public class SingleDCL{
//构造器私有
private SingleDCL(){}
//创建锁
private static volatile SingleDCL INSTANCE;
//编写公共方法获取私有锁
public static SingleDCL getInstance(){
//业务判断,逻辑判断
if(INSTANCE==null){//第一次检查
synchronized(SingleDCL.class){//加锁
if(INSTANCE==null){//第二次检查
INSYANCE=new SingleDCL();
}
}
return INSTANCE;
}
}
}
DCL需不需要加Volatile???
答案肯定是需要!!!必须!!!因为Volatile禁止指令重排序。
首先了解对象的创建过程
①NEW: 在内存中申请一块空间
当我new这个对象的时候,内存中已经有了一块空间,成员变量也有了,它的值是类型默认的值,如果类型是boolean,那它的值就是false,如果类型是引用类型,那么它的值是null。
②INVOKESPECIAL:调用特殊的方法(T的构造方法)
调用构造方法之后,才会给变量赋值。
③ASTORE 1:建立关联
将指针指向已经调用完构造方法的空间。
在对象创建的过程中,成员变量的值是在调用构造方法时才给的,所以说有个半初始化的过程。
当一个线程进来的时候,判断对象是否为空?肯定为空,因为还没创建呢,往下执行,拿到锁,继续往下执行,再次判断是否为空?为空,往下执行,在new对象的时候,对象有个半初始化的一个状态,在执行完new的时候,分配了一块空间,成员变量是引用类型那么它的值为null,就在此时,invokespecial和astore 1发生了指令重排序,直接将INSTANCE指向了初始化一半还没有调用构造方法的内存空间,这时候第二个线程进来了,判断对象为空吗?不为空,为啥?因为它指向了一个半初始化的一个对象嘛!既然不为空,我就直接返回了这个初始化一半的对象。不进行赋值操作,
所以说,这段代码要不要加volatile?必须加!加了volatile的这块内存,对于它的读写访问不可以重排序!
Volatile三大特性:
并发编程的三大特性:可见性,原子性,有序性、
Volatile保证可见性与有序性,但不保证原子性,保证原子性需借助synchronized这样的锁机制
①保证可见性
通过前面对 JMM 的介绍,我们知道,各个线程对主内存中共享变量的操作都是各个线程各自拷贝到自己的工作内存进行操作后在写回到主内存中的。
这就可能存在一个线程A修改了共享变量X的值,但是还未写入主内存时,另外一个线程B又对主内存中同一共享变量X进行操作,但此时A线程工作内存中共享变量X对线程B来说是不可见,这种工作内存与主内存同步延迟现象就造成了可见性问题。
public class TestVolatile {
boolean status = false;
/**
* 状态切换为t