单例模式之双重校验锁
public class SingletonDoubleKey { /** * 加volatile得原因:内存模型允许所谓的“无序写入” * singletonDoubleKey = new SingletonDoubleKey(); * 该语句非原子操作,实际是三个步骤。 * 1.给singletonDoubleKey分配内存 * 2.调用SingletonDoubleKey 的构造函数来初始化成员变量; * 3.将给singletonDoubleKey对象指向分配的内存空间(此时singleton才不为null); * * 执行命令时虚拟机可能会对以上3个步骤交换位置 最后可能是 1 3 2 这种 分配内存并修改指针后未初始化 多线程获取时可能会出现问题。 * 当线程A进入同步方法执行singletonDoubleKey = new SingletonDoubleKey();代码时,恰好这三个步骤重排序后为1 3 2, * 那么步骤3执行后singletonDoubleKey已经不为null,但是未执行步骤2,singletonDoubleKey对象初始化不完全,此时线程B执行getInstance()方法,第一步判断是 * singletonDoubleKey不为null,则直接将未完全初始化的singletonDoubleKey对象返回了。 */ private static volatile SingletonDoubleKey singletonDoubleKey = null; private SingletonDoubleKey(){} /** * 双重校验单例 * 两次if(singletonDoubleKey == null)原因: * 1.线程A进入 getInstance() 方法 * 2.由于 singletonDoubleKey为 null,线程A在 //2 处进入 synchronized 块。 * 3.线程A被线程B预占。 * 4.线程B进入 getInstance() 方法。 * 5.由于singletonDoubleKey仍旧为 null,线程B试图获取 //2 处的锁。然而,由于线程A已经持有该锁,线程B在 //2 处阻塞。 * 6.线程B被线程A预占 * 7.线程A执行,由于在 //3 处实例仍旧为 null,线程A还创建一个 SingletonDoubleKey 对象并将其引用赋值给 singletonDoubleKey。 * 8.线程A退出 synchronized 块并从 getInstance() 方法返回实例。 * 9.线程A被线程B预占。 * 10.线程B获取 //2 处的锁并检查 singletonDoubleKey 是否为 null。 * 11.由于 singletonDoubleKey是非 null 的,并没有创建第二个 Singleton 对象,由线程A所创建的对象被返回。 * @return */ public static SingletonDoubleKey getInstance(){ if(singletonDoubleKey == null){ // 1 synchronized (SingletonDoubleKey.class){ // 2 if(singletonDoubleKey == null){ // 3 singletonDoubleKey = new SingletonDoubleKey();// 4 } } } return singletonDoubleKey; } }