手撕单例模式

46 阅读1分钟

单例模式

保证单例对象的类只有一个实例

优点:不会频繁地创建和销毁对象,浪费系统资源

  • 饿汉式:提前创建好 static 对象
// 饿汉式单例
class Hungry {
    // 私有化构造器,不能new
    private Hungry(){

    }

    private final static Hungry HUNGRY = new Hungry();

    public static Hungry getInstance() {
        return HUNGRY;
    }
}
  • 双检索懒汉式
/**
 * 指令重排避免
 * 双重检测锁 + 原子性操作
 * 无视私有的构造器
 * 反射可以破环单例
 * 反射不能破环枚举的单例
 * 枚举中没有无参构造
 */

class LazyMan {

    // 抵制反射
    private static boolean mark = false;

    private LazyMan() {
        synchronized (LazyMan.class){
            if (mark == false) {
                mark = true;
            } else {
                throw new RuntimeException("不要试图用反射破环异常");
            }
        }
    }
    // 避免指令重排  vllatile
    private volatile static LazyMan lazyMan;

    // 双重锁检测 DCL 懒汉式
    public static LazyMan getInstance() {
        if (lazyMan == null) {
            synchronized(LazyMan.class){
                if (lazyMan == null) {
                    lazyMan = new LazyMan();  
                    // 创建对象的过程不是一个原子性操作  1. 分配内存空间  2. 执行构造方法,初始化对象                                                                 
                    // 3. 把这个对象引用指向这个空间
                }
            }
        }
        return lazyMan;
    }
}
  • 静态内部类
class Hungry {
    // 私有化构造器,不能new
    private Hungry(){

    }

    private final static Hungry HUNGRY = new Hungry();

    public static Hungry getInstance() {
        return HUNGRY;
    }
}

常见场景:

  1. 在Servlet编程中,每个Servlett也是单例的
  2. 在Spring中,每个Bean默认就是单例的