单例模式 – 安全地保有一份实例

单例模式

工具类、内存类、网络类、大模型预加载等场景中,公用性的这类对象仅需要一份实例在堆内存中,因此诞生出单例模式

线程安全的饿汉式实现

静态常量在类加载时创建,从始至终保有单例

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

线程不安全的懒汉式实现

懒汉式的想法是未使用不创建,只有第一次使用时才创建,之后一直保有单例。但是此方法是线程不安全的,当线程A刚刚执行完(1)时,线程B此时执行到(1)还处于未创建状态,因此也会去执行(2),最终将会创建两份实例

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

线程安全的懒汉式

方法加锁保证线程安全和单例,锁粒度较大造成性能下降
线程安全的懒汉式可以形象化为一个场景:所有人在门外排队办业务,每次只允许进入一个人,进去后大门上锁。当日办理名额已经满员时,排队的所有人仍然要依次进大门去尝试办理

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

双重校验锁懒汉式

减小锁粒度,优先保证读取性能,只有写时才加锁。使用volatile不仅是为了写操作对其他线程的可见性,同时是为了禁止指令重排序:new Singleton()的实现逻辑并不是原子的:分配内存、初始化、引用赋值,这三个操作可能被重排序先引用赋值,使得if(instance == null)校验不通过而返回空instance

双重校验锁可以形象化为一个场景:所有人在进门后排队等待到业务窗口办业务,窗口每次只服务一个人。当日办理名额已经满员时,只有排队的人会尝试再去办,而新来的人在门口就知道名额已满直接离去

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

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

©2018-2024 Howell版权所有 备案号:冀ICP备19000576号