单例模式


单例模式确保一个类只有一个实例,并提供一个全局访问点

类图

线程安全问题

有线程安全问题的单例模式:

public class Singleton {
 
    private static Singleton instance;
 
    private Singleton(){}
 
    public static Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

上面代码会有一个问题,当多个线程同时调用 getInstance() 方法时,可能会产生多个instance 实例,因此这种方式并不是真正的单例。

所以我们实现单例模式的核心就在于两点:

  1. 保证系统中只有一个实例
  2. 解决获取单例时线程安全

饿汉式

虚拟机加载时直接实例化好:

private static Singleton instance = new Singleton();

懒汉式

需要的时候才创建实例,但需要加synchronized锁来保证线程安全:

public static synchronized Singleton getInstance() {
    if (instance == null) {
        instance = new Singleton();
    }
    return instance;
}

这种方式有个问题,每次执行getInstance()都会加锁,操作比较重,实际上我们只需要实例化的那一刻加锁就可以了

双重校验锁

public class Singleton {

    private volatile static Singleton instance;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

这种方式有两个关键点:

  1. instance要使用volatile关键字修饰,可以防止JVM指令重排
  2. if (instance == null)这行有可能多个线程同时执行从而导致被多次实例化,因此synchronized同步块中需要再做一次判断

静态内部类

public class Singleton {

    private Singleton() {
    }

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

这种写法有一个特点,内部静态类SingletonHolder默认情况不会被JVM加载,只有当getInstance()被调用时,SingletonHolder才会被加载。

这种方式的线程安全是由JVM来确保

枚举方式

class Singleton{
}
 
public enum SingletonEnum {
    INSTANCE;
    
    private Singleton instance;
    
    SingletonEnum() {
        instance = new Singleton();
    }
    
    public Singleton getInstance() {
        return instance;
    }
}

当我们定义了一个SingletonEnum类型的枚举INSTANCE,JVM帮我们创建好了INSTANCE实例,而我们在构造方法中初始化了Singleton实例。

这种方式其实是利用了一个枚举值只会被初始化一次的这种特性,从而保证了单例

文章作者: 周君
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 周君 !
评论