单例模式的实现

       单例模式估计是咱们碰到最多也是最简单的一种设计模式了(也是面试中经常会遇到的面试题)。单例模式保证一个类只有一个实例,比如咱们在Android应用中登入成功之后保存用户信息就会优先考虑单例模式。

       单例模式有六种常规的写法:饿汉式、懒汉式(线程不安全)、懒汉式(线程安全)、DCL双重校验模式、静态内部类、枚举。

一、饿汉式

       饿汉式,有两种不同的写法:静态常量、静态代码块。

       饿汉式-静态常量

public class Singleton {

    private Singleton() {
    }

    /**
     * 静态常量
     */
    private final static Singleton INSTANCE = new Singleton();

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

       饿汉式-静态代码块

public class Singleton {

    private Singleton() {
    }

    /**
     * 静态代码块
     */
    private static Singleton INSTANCE;
    static {
        INSTANCE = new Singleton();
    }

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

       饿汉式实现单例有两种方式:静态常量、静态代码块。两种写法不管是在性能上还是在效果上都是一样的,都是在类装载的时候完成实例化。类装载的时候JVM保证了线程安全。

       特点:没有懒加载(类装载的时候就初始化)、线程安全。不推荐使用

二、懒汉式(线程不安全)

public class Singleton {

    private Singleton() {
    }

    private static Singleton INSTANCE;

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

       这种写法在单线程的程序中没啥问题,在第一次调用getInstance()方法的时候创建实例(懒加载)。但是在多线程的情况下多个线程同时调用getInstance()函数的时候在同一时刻这些线程都通过了if (INSTANCE == null)的判断,那就有可能重复创建了多个实例了。

       特点:懒加载(getInstance的时候才初始化)、线程不安全。不推荐使用

三、懒汉式(线程安全)

public class Singleton {

    private Singleton() {
    }

    private static Singleton INSTANCE;

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

       这种写法给getInstance()方法添加了synchronized,告诉JVM这是个同步方法。当多个线程同时调用getInstance()方法时,同一时刻只能有一个线程通过getInstance()方法,其他的线程只能等待。所以这种方式效率相对来说比较低。

       特点:懒加载(getInstance的时候才初始化)、线程安全、效率低(多个线程同时getInstance的时候会有等待的情况)。不推荐使用

懒汉式里面的懒字是懒加载的懒。为了和饿汉式对应有的地方也把懒汉式叫做饱汉式。

四、DCL双重校验模式(推荐)

public class Singleton {

    private Singleton() {
    }

    private volatile static Singleton INSTANCE;

    public static Singleton getInstance() {
        if (INSTANCE == null) { //第一次校验
            synchronized (Singleton.class) {
                if (INSTANCE == null) { //第二次校验
                    INSTANCE = new Singleton();
                }
            }
        }
        return INSTANCE;
    }
}

       这种写法在懒汉式(线程安全)方式之上做了进一步的提高。进行了两次if (INSTANCE == null)检查:第一次判断是为了避免不必要的同步从而提高效率、第二次判断之前添加了synchronized同步代码块保证了同一时刻只有一个线程能进来创建实例。

       特点:懒加载、线程安全、效率高。推荐使用

五、静态内部类(推荐)

public class Singleton {

    private Singleton() {
    }

    /**
     * 静态内部类
     */
    private static class SingletonInstance {

        private static final Singleton INSTANCE = new Singleton();
    }

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

       这种实现方式在Singleton类被装载时并不会实例化,只有在第一次调用getInstance()方法的时候才会装载内部类SingletonInstance从而完成Singleton的初始化,JVM保证了在类进行初始化时,别的线程是无法进入的。所以静态内部类实现单例的方法是线程安全的。而且还起到了懒加载的作用。

       特点:懒加载、线程安全。推荐使用

六、枚举(推荐)

       枚举可以有自己的属性,也可以有自己的方法。

       第一种写法(代码简洁,强烈推荐):

这里我假设mUserName来我要在单例里面保存的一个属性,之后我们可以通过SingletonEnum.INSTANCE.getUserName();来操作单例里面的属性。

public enum SingletonEnum {
    INSTANCE;

    private String mUserName;

    public String getUserName() {
        return mUserName;
    }

    public void setUserName(String userName) {
        mUserName = userName;
    }
}

       第二种写法,我们额外的多一个Singleton类,Singleton类里面放我们单例要保存的一些信息。调用的时候通过SingletonEnum.INSTANCE.getInstance()获取到Singleton类:

额外的Singleton类

public class Singleton {

}
public enum SingletonEnum {
    INSTANCE;

    private Singleton instance;

    SingletonEnum() {
        instance = new Singleton();
    }

    public Singleton getInstance() {
        return instance;
    }
}

       枚举实例JVM保证了线程安全,并且在任何情况下,它都是一个单例。同时枚举也是在第一次使用的时候初始化,这样也保证了懒加载。

       特点:懒加载、线程安全。推荐使用


       综上所述,综合效率,安全等因素对于单例模式实现的选择,我们优先推荐枚举(代码简洁),其次静态内部类,最后是DCL双重校验模式。

你可能感兴趣的:(单例模式的实现)