单例模式属于创建型模式,用于在系统运行过程中保证只有一个实例。

创建单例面临的问题有:反射破坏单例,多线程并发破坏、序列化破坏单例

饿汉式单例

/**
 * 懒汉式单例
 * 已解决反射破坏、序列化问题
 * 类加载时就创建了单例,不存在线程安全问题
 */
public class HungrySingleton {
    // 类加载时就初始化
    private static HungrySingleton hungrySingleton = new HungrySingleton();
    // 构造方法私有化
    private HungrySingleton(){
        // 防止反射破坏单例
        if (hungrySingleton != null) {
            throw new RuntimeException("非法访问");
        }
    }
    // 全局访问点
    public static HungrySingleton getInstance() {
        return hungrySingleton;
    }
    // 防止反序列化破环单例,ObjectInputStream桥接的方法。在反序列化时会调用readResolve方法返回单例
    public HungrySingleton readResolve() {
        return hungrySingleton;
    }
}

懒汉式单例

/**
 * 懒汉式单例
 * 双重检查解决并发问题
 * 可以被反射破坏、反序列化破坏
 */
public class LazySingleton {
    // 加volatile,防止指令重排序问题。保证线程间可见性
    private volatile static LazySingleton lazySingleton;
    // 构造方法私有化
    private LazySingleton(){}
    // 静态方法方式:
    private static LazySingleton getInstance() {
        if (lazySingleton == null) {
            synchronized (LazySingleton.class) {
                if (lazySingleton == null) {
                    lazySingleton = new LazySingleton();
                }
            }
        }
        return lazySingleton;
    }
}

容器式单例

枚举式单例、注册式单例、线程隔离的单例

枚举式单例

Jdk提供的Enum类型

/**
 * 枚举式单例
 * 容器式,但在类装载时就初始化占用内存。是线程安全的
 * 不可被反射破坏,JDK层禁止反射创建枚举类型对象。
 * 不可被反序列化破坏,JDK底层限制can't deserialize enum
 */
public enum EnumSingleton {
    INSTANCE;
    public static EnumSingleton getInstance() {
        return INSTANCE;
    }
}

/**
 * spring容器式单例
 * 在类getInstance使用时才装载到内存中。
 * 
 * 反射破坏,容器本身会被反射破坏,必要时采取保护措施
 * 反序列化破坏,容器本身会被反射序列化破坏,必要时采取保护措施
 * Map中只会保存一个类的实例,但不是绝对场景的线程安全。当第一次初始化时单例时两个线程同时进入就可能存在put被覆盖。必要时采取保护措施
 */
public class IocSington {
    // 初始化一个容器,用于装载各种类的单例
    private static Map<String, Object> ioc = new HashMap<>();
    // 构造方法私有化
    private IocSington(){}
    // 根据需要的对象类型,获取对应的实例
    public static Object getInstance(Class clazz) {
        String name = clazz.getName();
        if (!ioc.containsKey(name)) {
            Constructor constructor = clazz.getConstructor(null);
            constructor.setAccessible(true);
            Object o = constructor.newInstance();
            ioc.put(name, o);
        }
        return ioc.get(name);
    }
}

  

posted @ 2021-12-30 08:51  郎志刚  阅读(100)  评论(0)    收藏  举报