Copy from IDE

// Eager initialization
public class Singleton { // A singleton is someone who is neither married nor in a long-term relationship.
    // 饿汉式
    private static final Singleton INSTANCE = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() {
        return INSTANCE;
    }
}
 
// Lazy Initialization
class SingletonLazy {
    private static SingletonLazy INSTANCE;
    private SingletonLazy() {}
    public static SingletonLazy getInstance() {
        if(INSTANCE == null){
            INSTANCE = new SingletonLazy(); // 线程不安全
        }
        return INSTANCE;
    }
 
    // 改进加入synchronized 效率低
    public synchronized static SingletonLazy getInstanceImprove() {
        if(INSTANCE == null){
            INSTANCE = new SingletonLazy(); 
        }
        return INSTANCE;
    }
}

把SingletonLazy的getInstance()继续改进

public static SingletonLazy getInstanceImprove() {
		if(INSTANCE == null){ // 不过这样还不完美,因为这样还是有可能多个线程同时判断为 null 而进入等锁的状态
				synchronized(SingletonLazy.class){
					INSTANCE = new SingletonLazy(); 
				}
		}
		return INSTANCE;
    }

加一层内层判断,为什么?
因为:

  1. 线程 A 进入 getInstance(),发现 INSTANCE == null,进入 synchronized。
  2. 线程 B 也几乎同时进入,发现 INSTANCE == null,也等待获取锁。
  3. 线程 A 执行 INSTANCE = new SingletonLazy(),完成初始化,退出同步块。
  4. 线程 B 获得锁,进入 synchronized,也执行 INSTANCE = new SingletonLazy(),再次创建!

即使加了 synchronized,每个进入同步块的线程都会执行 new,除非你再检查一次 INSTANCE 是否已经被初始化了。

所以要双重判断

public static SingletonLazy getInstance(){
    if(INSTANCE == null) {
        synchronized (SingletonLazy.class) {
            if(INSTANCE == null) INSTANCE = new SingletonLazy();  //内层还要进行一次检查,双重检查锁定
        }
    }
    return INSTANCE;
}

这还是有问题:
可以看到,这种情况下,IDEA会要求我们添加一个volatile给INSTANCE,各位还记得这个关键字有什么作用吗?没错,我们还需要保证INSTANCE在线程之间的可见性,这样当其他线程进入之后才会拿INSTANCE由其他线程更新的最新值去判断,这样,就差不多完美了。

class SingletonLazy {
    private static volatile SingletonLazy INSTANCE;
    private SingletonLazy() {}
	 public static SingletonLazy getInstance(){
		if(INSTANCE == null) {
				synchronized (SingletonLazy.class) {
						if(INSTANCE == null) INSTANCE = new SingletonLazy(); 
				}
		}
		return INSTANCE;
	}
}

那么,有没有一种更好的,不用加锁的方式也能实现延迟加载的写法呢?我们可以使用静态内部类:

public class Singleton {
    private Singleton() {}
 
    private static class Holder {   //由静态内部类持有单例对象,但是根据类加载特性,
    // 我们仅使用Singleton类时,不会对静态内部类进行初始化
        private final static Singleton INSTANCE = new Singleton(); // 链接阶段-准备 INSTANCE = null
    }
 
    public static Singleton getInstance(){   //只有真正使用内部类时,才会进行类初始化
        return Holder.INSTANCE;   //直接获取内部类中的
    }
}