1、懒汉式,线程不安全
Lazy 初始化,多线程不安全,实现难度低
public class Singleton {private static Singleton instance;private Singleton (){}public static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}}
2、懒汉式,线程安全
Lazy 初始化,多线程安全,实现难度低
- 优点:第一次调用才初始化,避免内存浪费
缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率
public class Singleton {private static Singleton instance;private Singleton (){}public static synchronized Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}}
3、饿汉式
不是 Lazy 初始化,多线程安全,实现难度低
- 优点:没加锁,执行效率高
- 缺点:类加载时就初始化,浪费内存
基于 classloader 机制避免多线程同步问题,instance 在类装载时就实例化,导致类装载的原因有很多,在单例模式中大多是调用 getInstance, 但也不能确定有其他方式(或静态方法)导致类装载,这时初始化 instance 没达到 lazy loading 效果
public class Singleton {private static Singleton instance = new Singleton();private Singleton (){}public static Singleton getInstance() {return instance;}}
4、双检锁/双重校验锁(DCL,double-checked locking)
Lazy 初始化,多线程安全,实现难度较复杂
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;}}
5、登记式/静态内部类
Lazy 初始化,多线程安全,实现难度低
- 只适用于静态域情况,双检锁方式可在实例域需要延迟初始化时使用
利用了 classloader 机制保证初始化 instance 时只有一个线程,跟饿汉式不同,饿汉式只要 Singleton 类被装载,instance 就被实例化(没达到 lazy loading 效果),而该方式是 Singleton 类被装载,instance 不一定被初始化,因为 SingletonHolder 类没被主动使用,只有通过显式调用 getInstance 方法时才会显式装载 SingletonHolder 类从而实例化 instance
public class Singleton {private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}private Singleton (){}public static final Singleton getInstance() {return SingletonHolder.INSTANCE;}}
6、枚举
不是 Lazy 初始化,多线程安全,实现难度低
- 实现单例模式的最佳方法,更简洁,自动支持序列化机制,绝对防止多次实例化
- 不能通过 reflection attack 调用私有构造方法
public enum Singleton {INSTANCE;public void whateverMethod() {}}
一般不建议使用前两中懒汉式,建议使用饿汉式。只在明确实现 lazy loading 效果时才使用登记式。如果涉及反序列化创建对象,可用枚举式。如有其他特殊需求,可用双检锁式
