问题1,为什么synchronized未禁止指令重排序,却可以保证有序性?
因为加锁之后,同一时间只有一个线程执行,相当于单线程。指令重排序的特点是可以保证串行语义一致,虽然不保证多线程间的语义也一致 。简单来说,指令重排序+单线程运行,可以保证有序性。
问题2,双重校验实现单例模式,已经用到了synchronized锁,既然synchronized原子性、可见性、有序性都能保证,为什么还要用volatile?
public class Singleton {private volatile static Singleton uniqueInstance;private Singleton() {}public static Singleton getUniqueInstance() {//先判断对象是否已经实例过,没有实例化过才进⼊加锁代码if (uniqueInstance == null) { //(1)//类对象加锁synchronized (Singleton.class) {if (uniqueInstance == null) {uniqueInstance = new Singleton();}}}return uniqueInstance;}
}
原因1,(1)处在synchronized代码块外,需要用volatile保证可见性;
原因2,uniqueInstance = new Singleton();
这段代码其实是分为三步执⾏:
- 为 uniqueInstance 分配内存空间
- 初始化 uniqueInstance
- 将 uniqueInstance 指向分配的内存地址
但是由于 JVM 具有指令重排的特性,执⾏顺序有可能变成 1->3->2。指令重排在单线程环境下不会出现问题,但是在多线程环境下会导致⼀个线程获得还没有初始化的实例。例如,线程 T1 执⾏了 1和 3,此时 T2 调⽤ getUniqueInstance () 后发现 uniqueInstance 不为空,因此返回uniqueInstance ,但此时 uniqueInstance 还未被初始化。
参考资料:
JavaGuide面试突击版
synchronized能不能保证有序性??