设计模式 —— 单例模式

198 阅读2分钟

提纲

  • 饿汉模式
  • 懒汉模式(线程不安全)
  • 懒汉模式(线程安全)
  • 双重检验模式
  • 静态内部类单例模式
  • 枚举模式
  • 反序列化导致重新创建对象

1. 饿汉模式

  • 类加载时就完成了实例化,避免多线程的同步问题,但是没有达到懒加载效果
  • 从始至终未使用过这个实例,会造成内存的浪费

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

    private Singleton() {

    }

    private static Singleton getInstance() {
        return instance;
    }

}

2. 懒汉模式(线程不安全)

第一次加载时实例化对象,在多线程时不能正常工作

public class Singleton {
    private static Singleton instance;

    private Singleton() {

    }

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

3. 懒汉模式(线程安全)

用于多线程,每次调用getInstance都需要进行同步,但是会造成同步开销


public class Singleton {
    private static Singleton instance;

    private Singleton() {

    }

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

4. 双重检验模式(DSL)

  • 第一次为了不必要的同步操作,第二次在singleton等于null才去创建实例,资源利用率高
  • 使用volatile 会造成性能的影响
  • 高并发的情况下会有缺陷,DSL会失效,建议静态内部类单例来替代DCL

public class Singleton {
    private volatile static Singleton instance;

    private Singleton() {

    }

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

5. 静态内部类单例模式

第一次加载Singleton类时并不会初始化,只有第一次加载SingletonHolder.sInstance方法时虚拟机才会加载SingletonHodler并初始化,既能保障线程安全,又能保证Singleton类的唯一性


public class Singleton {
    private Singleton() {

    }

    public static Singleton getInstance() {
        return SingletonHolder.sInstance;
    }

    private static class SingletonHolder {
        private static final Singleton sInstance = new Singleton();
    }
}

6.枚举单例

枚举单例的创建是线程安全的,并在任何情况下都是单例的

enum Singleton {
    SINGLETON;

    public void doSomething() {

    }
}

7.反序列化问题

反序列化操作提供readResolve方法,这个方法可以控制对象的反序列化

  1. 不能有参数 2. 返回要为Object类型的方法 3. 返回的值是单例实例的引用

在ObjectStraeamClass源码中,那怕有没有readResolve方法都会创建这个对象,只是将内存中指向的地址重新赋值到待返回的引用上

class Singleton implements Serializable {

    private Object readResolve() throws ObjectStreamException {
        return  instance;
    }

}