创建型设计模式——单例模式

211 阅读3分钟

1、介绍

单例设计模式是最简单的一种设计模式。它提供了一种创建对象的方法,确保只有单个对象被创建。在使用时直接访问而不需要实例化相应的对象。即单例模式只能有一个实例,且该实例必须由该类自己创建。

常用场景:

  • 数据库连接池
  • 多线程的线程池的设计一般采用单例模式,这是由于线程池要方便对池中的线程进行控制
  • EventBus的单例(DCL模式)
  • LayoutInflater类
  • 工具类对象

2、实现

单例模式的实现共有六种,分别是饿汉模式,懒汉模式(线程不安全),懒汉模式(线程安全),双重检查模式,静态内部类单例模式,枚举类型。

2.1、饿汉模式

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

饿汉模式是最简单的单例模式,在一开始类加载阶段就完成相应的实例化,基于类加载机制所以避免了多线程的同步,但同时如果在整个程序运行过程中都没有使用这个实例,那么就会造成内存的浪费。同时也没有实现懒加载的效果。

2.2、懒汉模式(线程不安全)

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

懒汉模式是在第一次调用时完成实例化,这样相较于饿汉模式避免了在整个程序运行过程中都没有使用这个实例所造成的内存浪费现象。但因为在创建时并没有加锁,所以多线程情况下,该模式不能正常工作。

2.3、懒汉模式(线程安全)

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

线程安全的懒汉模式在实例化时加了锁,那么就避免了在多线程时的不安全,同时也实现了懒加载。在getInstance方法中加了synchronized会使得效率降低,但往往多数时候并不需要同步。

2.4、DCL双重检查模式

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

DCL是最常用的一种单例模式,实现了懒加载,保证了线程安全,同时也避免了不必要的同步。在第一步判空时,判断是否需要实例化,第二步判空则为了进行同步,避免多线程问题。

2.5、静态内部类单例模式

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

饿汉模式在类加载时机会完成实例化,而该方法当类加载时,SingletonHolder 类并没有被装载,所以 instance 并不会被实例化。只有通过显式调用 getInstance ,才会装载 SingletonHolder 类,从而实例化 instance。这样一方面避免了线程不安全,同时又保证了类的唯一性。

2.6、枚举类型

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

默认枚举实例的创建是线程安全的,且保持单例。

3、比较

实现方式 是否懒加载 是否线程安全 难度
饿汉模式 容易
懒汉模式(线程不安全) 容易
懒汉模式(线程安全) 容易
DCL模式 复杂
静态内部类单例模式 容易
枚举单例 容易