2021-06-13_KS1_S11_单例模式
18、彻底玩转单例模式
需要了解的知识:饿汉式、DCL懒汉式,深究:单例模式、枚举
1、饿汉式单例,代码
-
// 饿汉式单例 public class HungrySingleton { // 可能会浪费空间 private byte[] data1 = new byte[1024*1024]; private byte[] data2 = new byte[1024*1024]; private byte[] data3 = new byte[1024*1024]; private byte[] data4 = new byte[1024*1024]; // 构造器私有化——只要是单例模式 private HungrySingleton() { } private final static HungrySingleton HUNGRYSINGLETON = new HungrySingleton(); public static HungrySingleton getInstance() { return HUNGRYSINGLETON; } }
2、DCL_懒汉式单例,代码:
1. 普通懒汉单例模式
-
懒汉式单例_基础版本:单线程中OK
// 懒汉式单例_基础版本:单线程中OK public class LazyMan_01 { // 构造器私有化 private LazyMan_01() { System.out.println(Thread.currentThread().getName() +"OK"); } // 提供一个实例,但并不创建对象 private static LazyMan_01 lazyMan_01; private static LazyMan_01 getInstance() { if (lazyMan_01 == null) { lazyMan_01 = new LazyMan_01(); } return lazyMan_01; } /* 懒汉式单例_基础版本:单线程中OK */ public static void main(String[] args) { LazyMan_01 instance1 = LazyMan_01.getInstance(); LazyMan_01 instance2 = LazyMan_01.getInstance(); LazyMan_01 instance3 = LazyMan_01.getInstance(); LazyMan_01 instance4 = LazyMan_01.getInstance(); LazyMan_01 instance5 = LazyMan_01.getInstance(); System.out.println(instance1.hashCode()); System.out.println(instance2.hashCode()); System.out.println(instance3.hashCode()); System.out.println(instance4.hashCode()); System.out.println(instance5.hashCode()); } }
-
懒汉式单例_基础版本:单线程下普通懒汉单例模式OK,但在多线程并发下有问题(暴露问题:但在多线程并发下有问题)
// 懒汉式单例_基础版本:单线程下普通懒汉单例模式OK,但在多线程并发下有问题(**暴露问题**:但在多线程并发下有问题) public class LazyMan_01 { // 构造器私有化 private LazyMan_01() { System.out.println(Thread.currentThread().getName() +"OK"); } // 提供一个实例,但并不创建对象 private static LazyMan_01 lazyMan_01; private static LazyMan_01 getInstance() { if (lazyMan_01 == null) { lazyMan_01 = new LazyMan_01(); } return lazyMan_01; } /* 懒汉单例OK:多线程并发下的问题暴露 */ public static void main(String[] args) { for (int i = 0; i < 5; i++) { new Thread(()->{ LazyMan_01.getInstance(); }).start(); } } }
2. 懒汉式单例_DCL
-
懒汉式单例_DCL(解决问题:多线程并发下的懒汉问题1;暴露问题:但在多线程并发下有指令重排问题)
懒汉式单例_DCL(**解决问题**:多线程并发下的懒汉问题1;**暴露问题**:但在多线程并发下有指令重排问题) public class LazyMan_02 { // 构造器私有化 private LazyMan_02() { System.out.println(Thread.currentThread().getName() +"OK"); } // 提供一个实例,但并不创建对象 private static LazyMan_02 lazyMan_02; /* 不是原子性操作: 1.分配内存空间 2.执行构造方法,初始化对象 3.把这个对象指向这个空间 123 321 A——指令重排 B——此时插入 lazyManSingleton 还没有完成构造 */ // 双层检测锁模式——DCL懒汉懒汉模式 private static LazyMan_01 getInstance() { if (lazyMan_01 == null) { lazyMan_01 = new LazyMan_01(); } return lazyMan_01; } public static void main(String[] args) { for (int i = 0; i < 5; i++) { new Thread(()->{ LazyMan_02.getInstance(); }).start(); } } }
-
懒汉式单例_DCL(解决问题:解决多线程并发下指令重排问题)
// 提供一个实例,但并不创建对象。volatile:对于同一个变量,在一个线程中值发生了改变,则在另一个线程中立即生效,可以大幅度避免线程重排的问题,但不排除极端情况 private volatile static LazyMan_02 lazyMan_02;
-
懒汉式单例_DCL(暴露问题:用反射能够破坏单例模式1)
// 懒汉式单例_DCL(**暴露问题**:用反射能够破坏单例模式1) public class LazyMan_03 { // 构造器私有化 private LazyMan_03() { System.out.println(Thread.currentThread().getName() +"OK"); } // 提供一个实例,但并不创建对象。volatile:对于同一个变量,在一个线程中值发生了改变,则在另一个线程中立即生效,可以大幅度避免线程重排的问题,但不排除极端情况 private volatile static LazyMan_03 lazyMan_03; // 双层检测锁模式——DCL懒汉懒汉模式 private static LazyMan_03 getInstance() { if (lazyMan_03 == null) { synchronized (LazyManSingleton.class) { if (lazyMan_03 == null) { lazyMan_03 = new LazyMan_03(); } } } return lazyMan_03; } public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { LazyMan_03 instance1 = LazyMan_03.getInstance(); Constructor<LazyMan_03> declaredConstructor = LazyMan_03.class.getDeclaredConstructor(null); declaredConstructor.setAccessible(true); LazyMan_03 instance2 = declaredConstructor.newInstance(); System.out.println(instance1.hashCode()); System.out.println(instance2.hashCode()); System.out.println(instance1 == instance2); } }
-
懒汉式单例_DCL(解决问题:通过在无参构造中加锁,解决反射破坏单例模式1)
// 构造器私有化 private LazyMan_03() { synchronized (LazyMan_03.class) { if (lazyMan_03 != null) { throw new RuntimeException("异常:不要试图使用反射破坏"); } } System.out.println(Thread.currentThread().getName() +"OK"); }
-
懒汉式单例_DCL(暴露问题:用反射能够进一步破坏单例模式2)
// 懒汉式单例_DCL(**暴露问题**:用反射能够进一步破坏单例模式2) public class LazyMan_04 { // 构造器私有化 private LazyMan_04() { synchronized (LazyMan_04.class) { if (lazyMan_04 != null) { throw new RuntimeException("异常:不要试图使用反射破坏"); } } System.out.println(Thread.currentThread().getName() +"OK"); } // 提供一个实例,但并不创建对象。volatile:对于同一个变量,在一个线程中值发生了改变,则在另一个线程中立即生效,可以大幅度避免线程重排的问题,但不排除极端情况 private volatile static LazyMan_04 lazyMan_04; // 双层检测锁模式——DCL懒汉懒汉模式 private static LazyMan_04 getInstance() { if (lazyMan_04 == null) { synchronized (LazyManSingleton.class) { if (lazyMan_04 == null) { lazyMan_04 = new LazyMan_04(); } } } return lazyMan_04; } public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Constructor<LazyMan_04> declaredConstructor = LazyMan_04.class.getDeclaredConstructor(null); declaredConstructor.setAccessible(true); LazyMan_04 instance1 = declaredConstructor.newInstance(); LazyMan_04 instance2 = declaredConstructor.newInstance(); System.out.println(instance1.hashCode()); System.out.println(instance2.hashCode()); System.out.println(instance1 == instance2); } }
-
懒汉式单例_DCL(解决问题:用反射能够进一步破坏单例模式2)
懒汉式单例_DCL(**解决问题**:用反射能够进一步破坏单例模式2) public class LazyMan_04 { private static boolean zhout = false; // 构造器私有化 private LazyMan_04() { synchronized (LazyMan_04.class) { if (zhout) { zhout = true; } else { throw new RuntimeException("异常:不要试图使用反射破坏"); } } System.out.println(Thread.currentThread().getName() +"OK"); } // 提供一个实例,但并不创建对象。volatile:对于同一个变量,在一个线程中值发生了改变,则在另一个线程中立即生效,可以大幅度避免线程重排的问题,但不排除极端情况 private volatile static LazyMan_04 lazyMan_04; // 双层检测锁模式——DCL懒汉懒汉模式 private static LazyMan_04 getInstance() { if (lazyMan_04 == null) { synchronized (LazyManSingleton.class) { if (lazyMan_04 == null) { lazyMan_04 = new LazyMan_04(); } } } return lazyMan_04; } public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Constructor<LazyMan_04> declaredConstructor = LazyMan_04.class.getDeclaredConstructor(null); declaredConstructor.setAccessible(true); LazyMan_04 instance1 = declaredConstructor.newInstance(); LazyMan_04 instance2 = declaredConstructor.newInstance(); System.out.println(instance1.hashCode()); System.out.println(instance2.hashCode()); System.out.println(instance1 == instance2); } }
-
懒汉式单例_DCL(暴露问题:用反射能够进二步破坏单例模式3)
// 懒汉式单例_DCL(**暴露问题**:用反射能够进二步破坏单例模式3) public class LazyMan_05 { private static boolean zhout = false; public static boolean isZhout() { return zhout; } // 构造器私有化 private LazyMan_05() { synchronized (LazyMan_05.class) { // if (lazyMan_05 != null) { if (zhout) { throw new RuntimeException("异常:不要试图使用反射破坏"); } else { zhout = true; } // } } System.out.println(Thread.currentThread().getName() +"OK"); } // 提供一个实例,但并不创建对象。volatile:对于同一个变量,在一个线程中值发生了改变,则在另一个线程中立即生效,可以大幅度避免线程重排的问题,但不排除极端情况 private volatile static LazyMan_05 lazyMan_05; // 双层检测锁模式——DCL懒汉懒汉模式 private static LazyMan_05 getInstance() { if (lazyMan_05 == null) { synchronized (LazyManSingleton.class) { if (lazyMan_05 == null) { lazyMan_05 = new LazyMan_05(); } } } return lazyMan_05; } public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException { Constructor<LazyMan_05> declaredConstructor = LazyMan_05.class.getDeclaredConstructor(null); declaredConstructor.setAccessible(true); LazyMan_05 instance1 = declaredConstructor.newInstance(); Field zhout = LazyMan_05.class.getDeclaredField("zhout"); zhout.setAccessible(true); zhout.set(instance1, false); LazyMan_05 instance2 = declaredConstructor.newInstance(); System.out.println(instance1.hashCode()); System.out.println(instance2.hashCode()); System.out.println(instance1 == instance2); } }
3、枚举类的单例
-
反射无法破坏枚举的单例
-
创建枚举类:
public enum EnumSingleton { INSTANCE; public EnumSingleton getInstance() { return INSTANCE; } }
-
创建测试类:报错=java.lang.NoSuchMethodException: com.zhout.single_zhout.EnumSingleton.()
class Test{ public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { EnumSingleton instance = EnumSingleton.INSTANCE; Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor(null); declaredConstructor.setAccessible(true); EnumSingleton instance2 = declaredConstructor.newInstance(); // java.lang.NoSuchMethodException: com.zhout.single_zhout.EnumSingleton.<init>() System.out.println(instance); System.out.println(instance2); } }
-
javap -p 反编译(查看IDEA的target)
E:\singleton-model\target\classes\com\zhout\single>javap -p EnumSingleton.class Compiled from "EnumSingleton.java" public final class com.zhout.single.EnumSingleton extends java.lang.Enum<com.zhout.single.EnumSingleton> { public static final com.zhout.single.EnumSingleton INSTANCE; private static final com.zhout.single.EnumSingleton[] $VALUES; public static com.zhout.single.EnumSingleton[] values(); public static com.zhout.single.EnumSingleton valueOf(java.lang.String); private com.zhout.single.EnumSingleton(); public com.zhout.single.EnumSingleton getInstance(); static {}; }
-
javap -p 反编译结果图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4tcK2sXv-1623586465778)(…/…/KS1_Step11_%E6%BA%90%E7%A0%81%E6%8E%A2%E7%A9%B6and%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/KS1_S11_%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F/KS1_S11_%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F.assets/1623572008472.png)]
-
最后用专业工具jad进行反编译,发现枚举类的构造函数入参有两个值(String.class,int.class);
-
更新测试类:
class Test{ public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { EnumSingleton instance = EnumSingleton.INSTANCE; Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor(String.class,int.class); declaredConstructor.setAccessible(true); EnumSingleton instance2 = declaredConstructor.newInstance(); // java.lang.NoSuchMethodException: com.zhout.single_zhout.EnumSingleton.<init>() System.out.println(instance); System.out.println(instance2); } }
-
输出报错信息:枚举类不能被反射实例化
Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
-
newInstance的源码
if ((clazz.getModifiers() & Modifier.ENUM) != 0) throw new IllegalArgumentException("Cannot reflectively create enum objects");
-
小结:反射无法破坏枚举的单例
-
4、静态内部类
-
静态内部类——因为反射也不是安全的
// 静态内部类——因为反射也不是安全的 public class Holder { // 构造器私有化——只要是单例模式 private Holder() { } public static Holder getInstance() { return TheInnerClass.HOLDER; } public static class TheInnerClass { private static final Holder HOLDER = new Holder(); } }