单例模式属于创建型模式,用于在系统运行过程中保证只有一个实例。
创建单例面临的问题有:反射破坏单例,多线程并发破坏、序列化破坏单例
饿汉式单例
/**
* 懒汉式单例
* 已解决反射破坏、序列化问题
* 类加载时就创建了单例,不存在线程安全问题
*/
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);
}
}

浙公网安备 33010602011771号