疑问
volatile
关键字和synchronized
关键字。
synchronized
可以保证原子性、有序性和可见性。
而volatile
却只能保证有序性和可见性。
注意:这两个有序的意思是不一样的。
那么,我们再来看一下双重校验锁实现的单例,已经使用了synchronized
,为什么还需要volatile
?这个volatile
是否可以去掉?
public class SingleInstance{
// 使用 volatile 禁止 new 对象时进行指令重排
// new 对象,有三条指令完成
// 1 JVM为对象分配一块内存M在堆区
// 2 在内存M上为对象进行初始化
// 3 将内存M的地址复制给instance变量
private volatile static SingleInstance instance;
private SingleInstance(){
// 反射可以直接调用构造方法,绕过private的限制
// 这里进行避免
// 反序列化咋搞?
if(instance != null){
throw new RuntimeException("single instance existed");
}
}
public SingaleInstance getInstance(){
// 减少锁竞争,避免过多的线程进入同步队列,进入 blocking 状态
if(instance == null){
// 保证只有一个线程可以实例化对象,其他线程进入同步队列blocking
// 这个 syn 可以保证原子性和可见性
// 而 有序性,指的是 保证线程串行进入syn 代码块内
// 所以,此有序性无法保证 syn代码块 内部的有序性
synchronized(SingleInstance.class){
// 避免重复创建对象
if(instance == null){
instance = new SingleInstance();
}
}
}
return instance;
}
}
由于 synchronized
是不能保证指令重排的,所以,可能会出问题。
new 对象的三个步骤(简化)
- JVM为对象分配一块内存M。
- 在内存M上为对象进行初始化。
- 将内存M的地址复制给singleton变量。
这个步骤有两种执行顺序可以按照 ①②③或者①③②来执行。
当我们按照①③②的顺序来执行的时候:
我们假设有两个线程ThreadA 和ThreadB同时来请求SingleInstance.getInstance
方法:
正常情况按照 ①②③的顺序来执行
- ThreadA 进入到同步代码块,执行
instance = new SingleInstance()
进行对象的初始化(按照对象初始化的过程 ①②③)执行完。 - ThreadB进入第5行判断
instance
不为空(已经初始化好了),直接返回instance
- 拿到这个对象做其他的操作。
这样看下来是不是没有啥问题。
但是,如果对象初始化的时候按照 ①③② 的步骤我们再来看看:
- ThreadA进入到同步代码块,执行
instance = new SingleInstance()
执行完; ①JVM为对象分配一块内存M。③将内存的地址复制给singleton变量。 - 此时ThreadB直接进入到同步代码块,发现
instance
已经不为空了然后直接就跳转到12行拿到这个instance
返回去执行操作去了。此时ThreadB拿到的singleton对象是个半成品对象,因为还没有为这个对象进行初始化(②还没执行)。 - 所以, ThreadB拿到的对象去执行方法可能会有异常产生。至于为什么会这样列?《Java 并发编程实战》有提到
有 synchronized 无 volatile 的 DCL(双重检查锁) 会出现的情况:线程可能看到引用的当前值,但对象的状态值确少失效的,这意味着线程可以看到对象处于无效或错误的状态。
说白了也就是ThreadB是可以拿到一个引用已经有了但是内存资源还没有分配的对象。
解决
解决指令重排只要给 instance 加个
volatile
修饰就好
synchronized
和 volatile
的有序性比较
synchronized
的有序性:是持有相同锁的两个同步块只能串行的进入,即被加锁的内容要按照顺序被多个线程执行,但是其内部的同步代码还是会发生重排序,使块与块之间有序可见。volatile
的有序性:是通过插入内存屏障来保证指令按照顺序执行。不会存在后面的指令跑到前面的指令之前来执行。是保证编译器优化的时候不会让指令乱序。
附录
https://blog.csdn.net/zengfanwei1990/article/details/110245035
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/69698.html