likes
comments
collection
share

单例模式

作者站长头像
站长
· 阅读数 24

什么是单例

何为单例模式,通俗的讲就是:让整个程序中,只有单一的一个实例。也就是说,我们在程序中不会创建多次对象实例,只会创建一次对象实例,且该单一的对象对整个程序共享。

所以,我们可以得出,单例模式的基本类图如下:

单例模式

而在实际运用中,我们有 6 种单例模式的写法。下面我们具体来讲一下。

饿汉模式

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

饿汉模式在类加载时候就完成初始化了,所以后续需要访问时候,他都是已经存在的了,这避免了线程同步的问题,使得其是线程安全的。

懒汉模式

懒汉模式指的是懒加载,让单例需要被使用到的时候再去加载,避免不必要的浪费。

线程不安全

public class Singleton {
    private static Singleton instance;
    private Singleton(){

    }

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

为什么这种是线程不安全的呢?因为可能存在两个线程同时判断该 instance 为空的情况,然后破坏了单例模式。

线程安全

public class Singleton {
    private static Singleton instance;
    private Singleton(){

    }

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

此处的代码相对于上述不安全的代码,多了一个在 getInstance() 方法中加入同步锁的代码,这样子理论上就可以实现线程安全了。

双重检查模式

上述懒汉模式中,在 getInstance() 中加入同步锁,会使得每次使用都同步,但是实际上,同步的情况是较少的。这样子会造成不必要的同步开销。并且,其同步并不一定真的同步了。

在基于偏序关系Happens-Before内存模型中,指令重排技术大大提高了程序执行效率,但同时也引入了一些问题。

竞态条件会导致instance引用被多次赋值,使用户得到两个不同的单例。

volatile关键字的作用、原理 - 猴子007 - 博客园 (cnblogs.com)

下面我们使用双重检查模式来解决上述的问题

 public class Singleton {
    private static volatile Singleton instance;
    private Singleton(){}
    public static Singleton getInstance() {
        if ( instance == null ) { //当instance不为null时,仍可能指向一个“被部分初始化的对象”
            synchronized (Singleton.class) {
                if ( instance == null ) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

第一次检查是为了不必要的同步,第二次检查是线程同步后,Singleton 等于 null 时候才创建实例。

静态内部类的单例模式

public class Singleton {
    private Singleton(){

    }

    public static synchronized Singleton getInstance(){
        
        return SingletonHolder.sInstance;
    }
    
    private static class SingletonHolder{
        private static final Singleton sInstance = new Singleton();
    }
}

这个单例模式,既可以实现懒加载,又可以实现线程安全。是类加载和懒加载的融合。

枚举单例

public enum Singleton {
    INSTANCE;
    public void doSomeThing(){
        
    }
}

枚举的肯定是单例且线程安全的,但是一般我们都不会用到这种方式。

综上,我们学习了六种单例模式,其中只有一类基本的懒加载是线程不安全的,我们要避免使用这种懒加载。

参考

volatile关键字的作用、原理 - 猴子007 - 博客园 (cnblogs.com)

《Android进阶之光》

转载自:https://juejin.cn/post/7235183781803409464
评论
请登录