目录
一、volatile变量的特性
参考:
volatile为什么不能保证原子性?_Ludwig__的博客-CSDN博客_volatile为什么不能保证原子性https://blog.csdn.net/qq_46190347/article/details/109908724Java volatile关键字最全总结:原理剖析与实例讲解(简单易懂)_老鼠只爱大米的博客-CSDN博客_java volatilehttps://blog.csdn.net/u012723673/article/details/80682208?spm=1001.2014.3001.5506
1、保证可见性,不保证原子性
可见性?
(1)读volatile:每当子线程某一语句要用到volatile变量时,都会从主线程重新拷贝一份,这样就保证子线程的会跟主线程的一致。
(2)写volatile: 每当子线程某一语句要写volatile变量时,都会在读完后同步到主线程去,这样就保证主线程的变量及时更新。
不保证原子性?
仅在单次读或者单次写这样的原子操作中,volatile能够实现线程安全。如果遇到 i++ 这种复合操作时(i++ 复合了读取、加、赋值3步),不能保证原子性。
2、禁止指令重排
(1)当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行;
(2)在进行指令优化时,不能将对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行。
什么是指令重排?参考:
java排序为什么会出现多次排序结果不一样_并发理论基础:指令重排序问题_向天再借十厘米的博客-CSDN博客https://blog.csdn.net/weixin_29734285/article/details/113083692?spm=1001.2014.3001.5506【Java 并发编程】指令重排序规范 ( happens-before 先行发生原则 )_韩曙亮的博客-CSDN博客_java指令重排序https://hanshuliang.blog.csdn.net/article/details/120186485?spm=1001.2014.3001.5506
二、单例模式(双重检查锁)
参考:
(这属于懒汉模式,初次使用时才实例化。【饿汉模式:一开始就实例化】)
public class TestInstance {
private volatile TestInstance instance;
public TestInstance getInstance() {
//1
if (instance == null) {
//2
synchronized (this) {
//3
if (instance == null) {
//4
instance = new TestInstance();
}
}
}
//6
return instance;
}
}
为什么要用同步锁synchronized就不赘述了,重点讲解为什么要用volatile?
步骤//4能被拆解成如下伪代码:
a.分配内存
memory = allocate()
b.初始化对象
ctorInstanc(memory)
c.设置instance指向刚分配的地址
instance = memory
如果instance不被volatile修饰,伪代码中abc指令可能会被重排序。假设有两个线程A和B,线程A先执行,执行到步骤//4时,指令被重排序成acb,线程A执行完指令c后,instance已经有指向的地址,instance不再为空,但该对象还未被初始化。此时,线程B执行,在步骤//1时,判断instance不为空,直接返回一个未被初始化的对象instance。综上所述,volatile可以用来防止指令重排序。