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;
}加一层内层判断,为什么?
因为:
- 线程 A 进入
getInstance(),发现INSTANCE == null,进入synchronized。 - 线程 B 也几乎同时进入,发现
INSTANCE == null,也等待获取锁。 - 线程 A 执行
INSTANCE = new SingletonLazy(),完成初始化,退出同步块。 - 线程 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; //直接获取内部类中的
}
}