目录
- 模式定义
- 应用场景
- 实现方式
- 1.懒汉模式:
- 2.饿汉模式:
- 3.静态内部类
- 反射
- 如何防止反射攻击破坏?
- 枚举类型
- 序列化
- 部分源码中的应用定位
- Spring & JDK
- Tomcat
- 反序列化指定数据源
模式定义
保证一个类只有一个实例,并且提供一个全局访问点
应用场景
重量级的对象,不需要多个实例,如线程池,数据库连接池
实现方式
1.懒汉模式:
延迟加载,只有在真正使用的时候,才开始实例化。
1)线程安全问题。
2)double check 加锁优化。
3)编译器(JIT),CPU 有可能对指令进行重排序,导致使用到尚未初始化的实例,可以通过添加volatile 关键字进行修饰,对于volatile 修饰的字段,可以防止指令重排。
public class LazySingletonTest {public static void main(String[] args) {
// LazySingleton instance1 = LazySingleton.getInstance();
// LazySingleton instance2 = LazySingleton.getInstance();
// System.out.println(instance1==instance2); //truenew Thread(() -> {LazySingleton instance1 = LazySingleton.getInstance();System.out.println(instance1);}).start();new Thread(() -> {LazySingleton instance2 = LazySingleton.getInstance();System.out.println(instance2);}).start();}
}class LazySingleton {private volatile static LazySingleton instance;private LazySingleton() {}public static LazySingleton getInstance() {if (instance == null) {synchronized (LazySingleton.class){if (instance == null) {instance = new LazySingleton();// 字节码层// JIT , CPU 有可能对如下指令进行重排序// 1. 分配空间 2. 初始化 3. 引用赋值}}}return instance;}
}
2.饿汉模式:
类加载的初始化阶段就完成了实例的初始化 。
本质上就是借助于jvm类加载机制,保证实例的唯一性(初始化过程只会执行一次)及线程安全(JVM以同步的形式来完成类加载的整个过程)。
类加载过程:
- 加载二进制数据到内存中, 生成对应的Class数据结构,
- 连接: a. 验证, b.准备(给类的静态成员变量赋默认值),c.解析
- 初始化: 给类的静态变量赋初值
只有在真正使用对应的类时,才会触发初始化 如( 当前类是启动类即main函数所在类,直接进行new 操作,访问静态属性、访问静态方法,用反射访问类,初始化一个类的子类等.)
public class HungrySingletonTest {public static void main(String[] args) {HungrySingleton instance = HungrySingleton.getInstance();HungrySingleton instance1 = HungrySingleton.getInstance();System.out.println(instance==instance1);}
}
class HungrySingleton {private static HungrySingleton instance=new HungrySingleton();private HungrySingleton(){}public static HungrySingleton getInstance(){return instance;}
}
3.静态内部类
1).本质上是利用类的加载机制来保证线程安全
2).只有在实际使用的时候,才会触发类的初始化,所以也是懒加载的一种形式。
public class InnerClassSingletonTest {public static void main(String[] args) {
// InnerClassSingleton instance = InnerClassSingleton.getInstance();
// InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
// System.out.println(instance==instance1); //truenew Thread(() -> {InnerClassSingleton instance = InnerClassSingleton.getInstance();System.out.println(instance);}).start();new Thread(() -> {InnerClassSingleton instance1 = InnerClassSingleton.getInstance();System.out.println(instance1);}).start();}
}
class InnerClassSingleton{private static class InnerClassHolder{private static InnerClassSingleton instance = new InnerClassSingleton();}private InnerClassSingleton(){}public static InnerClassSingleton getInstance(){return InnerClassHolder.instance;}
}
反射
public class InnerClassSingletonTest {public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// InnerClassSingleton instance = InnerClassSingleton.getInstance();
// InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
// System.out.println(instance==instance1); //true// new Thread(() -> {
// InnerClassSingleton instance = InnerClassSingleton.getInstance();
// System.out.println(instance);
// }).start();
//
// new Thread(() -> {
// InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
// System.out.println(instance1);
// }).start();Constructor<InnerClassSingleton> declaredConstructor = InnerClassSingleton.class.getDeclaredConstructor();declaredConstructor.setAccessible(true);InnerClassSingleton innerClassSingleton = declaredConstructor.newInstance();InnerClassSingleton instance = InnerClassSingleton.getInstance();System.out.println(innerClassSingleton==instance); //false}
}
class InnerClassSingleton{private static class InnerClassHolder{private static InnerClassSingleton instance = new InnerClassSingleton();}private InnerClassSingleton(){}public static InnerClassSingleton getInstance(){return InnerClassHolder.instance;}
}
如何防止反射攻击破坏?
饿汉模式下无法避免,懒汉模式可在构造函数中增加如下代码,判断实例是否存在。
private InnerClassSingleton(){if (InnerClassHolder.instance!=null){throw new RuntimeException("单例不允许多个实例");}}
public class InnerClassSingletonTest {public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// InnerClassSingleton instance = InnerClassSingleton.getInstance();
// InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
// System.out.println(instance==instance1); //true// new Thread(() -> {
// InnerClassSingleton instance = InnerClassSingleton.getInstance();
// System.out.println(instance);
// }).start();
//
// new Thread(() -> {
// InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
// System.out.println(instance1);
// }).start();Constructor<InnerClassSingleton> declaredConstructor = InnerClassSingleton.class.getDeclaredConstructor();declaredConstructor.setAccessible(true);InnerClassSingleton innerClassSingleton = declaredConstructor.newInstance();InnerClassSingleton instance = InnerClassSingleton.getInstance();System.out.println(innerClassSingleton==instance); //false}
}
class InnerClassSingleton{private static class InnerClassHolder{private static InnerClassSingleton instance = new InnerClassSingleton();}private InnerClassSingleton(){if (InnerClassHolder.instance!=null){throw new RuntimeException("单例不允许多个实例");}}public static InnerClassSingleton getInstance(){return InnerClassHolder.instance;}
}
枚举类型
1)天然不支持反射创建对应的实例,且有自己的反序列化机制
2)利用类加载机制保证线程安全
public enum EnumSingletonTest {INSTANCE;private void print(){System.out.println(this.hashCode());}
}class EnumTest{public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException, ClassNotFoundException {EnumSingletonTest instance = EnumSingletonTest.INSTANCE;
// EnumSingletonTest instance1 = EnumSingletonTest.INSTANCE;
// System.out.println(instance==instance1); // true// Constructor<EnumSingletonTest> declaredConstructor = EnumSingletonTest.class.getDeclaredConstructor(String.class,int.class);
// declaredConstructor.setAccessible(true);
// EnumSingletonTest instance = declaredConstructor.newInstance("INSTANCE",0);// ObjectOutputStream oss = new ObjectOutputStream(new FileOutputStream("enumsingleton"));
// oss.writeObject(instance);
// oss.close();ObjectInputStream ois = new ObjectInputStream(new FileInputStream("enumsingleton"));EnumSingletonTest object = ((EnumSingletonTest) ois.readObject());System.out.println(instance==object); //true:有自己的反序列化机制}
}
序列化
可以利用指定方法readResolve来替换从反序列化流中的数据 如下
ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;
ANY‐ACCESS‐MODIFIER Object readResolve() throws ObjectStreamException;
ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
public class InnerClassSingletonTest {public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException, ClassNotFoundException {
// InnerClassSingleton instance = InnerClassSingleton.getInstance();
// InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
// System.out.println(instance==instance1); //true// new Thread(() -> {
// InnerClassSingleton instance = InnerClassSingleton.getInstance();
// System.out.println(instance);
// }).start();
//
// new Thread(() -> {
// InnerClassSingleton instance1 = InnerClassSingleton.getInstance();
// System.out.println(instance1);
// }).start();// Constructor<InnerClassSingleton> declaredConstructor = InnerClassSingleton.class.getDeclaredConstructor();
// declaredConstructor.setAccessible(true);
// InnerClassSingleton innerClassSingleton = declaredConstructor.newInstance();
// InnerClassSingleton instance = InnerClassSingleton.getInstance();
// System.out.println(innerClassSingleton==instance); //falseInnerClassSingleton instance = InnerClassSingleton.getInstance();
// ObjectOutputStream oss = new ObjectOutputStream(new FileOutputStream("testSerializable"));
// oss.writeObject(instance);
// oss.close();ObjectInputStream ois = new ObjectInputStream(new FileInputStream("testSerializable"));InnerClassSingleton object = ((InnerClassSingleton) ois.readObject());System.out.println(instance==object);}
}
class InnerClassSingleton implements Serializable {static final long serialVersionUID =42L;private static class InnerClassHolder{private static InnerClassSingleton instance = new InnerClassSingleton();}private InnerClassSingleton(){if (InnerClassHolder.instance!=null){throw new RuntimeException("单例不允许多个实例");}}public static InnerClassSingleton getInstance(){return InnerClassHolder.instance;}Object readResolve() throws ObjectStreamException{return InnerClassHolder.instance;}
}
部分源码中的应用定位
感兴趣的可以去官网下载源码深入研究
Spring & JDK
java.lang.Runtime
org.springframework.aop.framework.ProxyFactoryBean
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry
org.springframework.core.ReactiveAdapterRegistry
Tomcat
org.apache.catalina.webresources.TomcatURLStreamHandlerFactory
反序列化指定数据源
java.util.Currency
创作不易,点个关注吧!!!
创作不易,点个关注吧!!!
创作不易,点个关注吧!!!