单例模式一共几种?以及优缺点,运行速度最快的是哪一种?

单例模式:单例模式的意思就是只有一个实例。单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类。

单例模式主要有三种:

分别是懒汉式单例,饿汉式单例,登记式单例(懒汉模式变种,使用静态内部类创建)。

1.懒汉式单例
/**
 * 这个是懒汉模式,线程安全的
 */
public class Singleton {
    private static Singleton singleton;
    private Singleton() {}  //此类不能被实例化
    // 加上 synchronized 保证创建线程线程安全
    public static synchronized Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

优点:第一次调用才初始化,避免内存浪费。
缺点:必须加锁synchronized 才能保证单例,(如果两个线程同时调用getInstance方法,会chuxia)但加锁会影响效率。

2.饿汉式单例
/**
 * 这个是饿汉模式,类一加载就要初始化
 */
public class SingletonDemo3 {

    // 在成员变量声明的时候就要初始化
    private static SingletonDemo3 instance = new SingletonDemo3();

    private SingletonDemo3() {

    }

    public static SingletonDemo3 getInstance() {
        // 若当前实例为空,重新指向一个新的实例
        if (instance == null) {
            instance = new SingletonDemo3();

        }
        return instance;
    }
}

优点:没有加锁,执行效率会提高。
缺点:类加载时就初始化,浪费内存。

3.登记式模式(holder)

内部类只有在外部类被调用才加载,产生Singleton实例;又不用加锁。此模式有上述两个模式的优点,屏蔽了它们的缺点,是最好的单例模式。

/**
 * 这个是懒汉模式变种,使用静态内部类创建
 */
public class SingletonDemo4 {

    private static class SingletonHolder{
        private static final SingletonDemo4 instance = new SingletonDemo4();
    }

    private SingletonDemo4() {

    }

    public static SingletonDemo4 getInstance() {
        // 调用内部类的属性创建新的实例
        return SingletonHolder.instance;
    }
}

静态内部类这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,它跟前几种方式不同的是:Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。想象一下,如果实例化instance很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。这个时候,这种方式相比饿汉方法就显得更合理。

若单例对象占用资源少,不需要延迟加载,那么使用枚举好于饿汉式,因为枚举十分安全,耗时差不多。

若单例对象占用资源多,需要延迟加载,那么使用内部类好于懒汉式,耗时差距明显。