单例设计模式


单例模式属于创建型模式,用于在系统运行过程中保证只有一个实例。
创建单例面临的问题有:反射破坏单例,多线程并发破坏、序列化破坏单例

1 饿汉式单例

/**
 * 懒汉式单例
 * 已解决反射破坏、序列化问题
 * 类加载时就创建了单例,不存在线程安全问题
 */
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;
    }
}

2 懒汉式单例

/**
 * 懒汉式单例
 * 双重检查解决并发问题
 * 可以被反射破坏、反序列化破坏
 */
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;
    }
}
/**
 * 懒汉式单例,静态内部类加载的方式
 * 是线程安全的
 * 可以被反射破坏、反序列化破坏
 */
public class LazyInnerSingleton {
    // 构造方法私有化
    private LazyInnerSingleton(){}

    public static LazyInnerSingleton getInstance() {
        return LazyInner.LAZY_INNER_SINGLETON;
    }
    // 静态内部类在使用时再加载,未使用时不浪费内存
    private static class LazyInner {
        private static final LazyInnerSingleton LAZY_INNER_SINGLETON = new LazyInnerSingleton();
    }
}

3 容器式单例

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

3.1 枚举式单例

Jdk提供的Enum类型

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

3.2 注册式单例

spring的利用ioc容器存储单例的形式

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

3.3 线程隔离式单例

利用ThreadLocal类为每个线程提供线程间相互隔离的单例

/**
 * 线程隔离的单例。
 * 容器式单例,一个线程创建一个单例
 * 该场景内是线程安全的。
 * 反射破坏,容器本身会被反射破坏,必要时采取保护措施
 * 反序列化破坏,容器本身会被反射序列化破坏,必要时采取保护措施
 */
public class ThreadLocalSingleton {
    private static final ThreadLocal<ThreadLocalSingleton> threadLocal
            = new ThreadLocal<ThreadLocalSingleton>(){
        @Override
        protected ThreadLocalSingleton initialValue() {
            return new ThreadLocalSingleton();
        }
    };
    private ThreadLocalSingleton(){}

    public static ThreadLocalSingleton getInstance() {
        return threadLocal.get();
    }
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值