制作html5网站,广告公司运作模式,做早餐的网站,长治在百度做个网站多少钱文章目录1. 饿汉式2. 懒汉式3. DCL 双重校验锁懒汉式4. 通过反射破坏DCL 加锁阻止5. 通过不调用 getInstance() 来破坏单例6. 通过反射来干扰信号量#xff0c;从而破坏单例7. 通过枚举类实现单例#xff0c;可以防止反射破坏单例学 JUC 的时候顺便摸了下单例模式…
文章目录1. 饿汉式2. 懒汉式3. DCL 双重校验锁懒汉式4. 通过反射破坏DCL 加锁阻止5. 通过不调用 getInstance() 来破坏单例6. 通过反射来干扰信号量从而破坏单例7. 通过枚举类实现单例可以防止反射破坏单例学 JUC 的时候顺便摸了下单例模式看的是狂神的JUC教程~
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 static HungrySingleton INSTANCE new HungrySingleton();// 私有化构造函数private HungrySingleton() {}// 获取实例的入口public static HungrySingleton getInstance() {return INSTANCE;}public static void main(String[] args) {System.out.println(HungrySingleton.getInstance());}
}2. 懒汉式
避免了空间的浪费单线程安全但是多线程不安全。
public class LazySingleton {// 懒起来了用到才实例化private static LazySingleton INSTANCE null;private LazySingleton(){}// 单线程没事但是多线程情况不安全public static LazySingleton getInstance(){if(INSTANCE null){INSTANCE new LazySingleton();}return INSTANCE;}
}3. DCL 双重校验锁懒汉式
Double Check Lock判断了两次加了一个 synchronized 锁住代码块为什么要判断两次可能多个进程卡在 synchronized 锁这步所以进去后还要再判断一次不安全的原因指令重排见代码注释解决方法加 volatile 关键字禁止指令重排见第三行注释代码
public class DCLSingleton {private static DCLSingleton INSTANCE null;// private volatile static DCLSingleton INSTANCE null;private DCLSingleton(){}// 双重校验锁public static DCLSingleton getInstance(){if(INSTANCE null){synchronized (DCLSingleton.class){if(INSTANCE null){// 但是实际上这一行代码还是不安全的// 因为可能会指令重排导致 return 未初始化完成的 INSTANCE// 解决方法是给 INSTANCE 加上一个 volatile 关键字禁止指令重排如注释INSTANCE new DCLSingleton();// 然而即使如此还是可以用反射来破坏单例模式}}}return INSTANCE;}
}从字节码指令角度分析指令重排影响(图源黑马JVM视频侵删可以看到JIT 编译器可能会优化先 putstatic把引用地址赋予 INSTANCE然后再 Method 来初始化但是并发情况下可能在赋予引用后init 前有其他线程访问了 getInstance()获得了一个还未引用的 INSTANCE导致出错。
4. 通过反射破坏DCL 加锁阻止
可以通过反射 setAccessible 无视私有使用构造器来破坏单例见 main() 代码阻止方法在构造器再加一个 synchronized 锁并且进行反射破坏判断并抛出异常
public class TripleSingleton {private static TripleSingleton INSTANCE null;private TripleSingleton(){// 再加一重锁防止反射破坏单例synchronized (TripleSingleton.class){if(INSTANCE ! null){throw new RuntimeException(不要通过反射来破坏单例模式);}}}// 三重校验锁public static TripleSingleton getInstance(){if(INSTANCE null){synchronized (TripleSingleton.class){if(INSTANCE null){INSTANCE new TripleSingleton();}}}return INSTANCE;}public static void main(String[] args) throws Exception{System.out.println(TripleSingleton.getInstance());// 使用反射来破坏单例模式null 代表无参构造器ConstructorTripleSingleton constructor TripleSingleton.class.getDeclaredConstructor(null);// 无视私有破坏单例模式constructor.setAccessible(true);System.out.println(constructor.newInstance());}
}5. 通过不调用 getInstance() 来破坏单例
既然 4 是基于 INSTANCE ! null 的情况来判断那么我们只要不理 INSTANCE 就可以破坏单例模式了只通过反射得到的构造器来创造多个实例解决方法加一个信号量 flag在构造函数中防止这种破坏方式
public class TripleSingleton2 {private static TripleSingleton2 INSTANCE null;// 再加一个信号量防止破坏单例private static boolean flag false;private TripleSingleton2(){// 再加一重锁防止破坏单例synchronized (TripleSingleton2.class){if(flag false){flag true;}else {throw new RuntimeException(不要通过反射来破坏单例模式);}}}// 三重校验锁public static TripleSingleton2 getInstance(){if(INSTANCE null){synchronized (TripleSingleton2.class){if(INSTANCE null){INSTANCE new TripleSingleton2();}}}return INSTANCE;}public static void main(String[] args) throws Exception{// 不调用getInstance()直接反射多个实例的情况// 使用反射来破坏单例模式null 代表无参构造器ConstructorTripleSingleton2 constructor TripleSingleton2.class.getDeclaredConstructor(null);// 无视私有破坏单例模式constructor.setAccessible(true);System.out.println(constructor.newInstance());System.out.println(constructor.newInstance());}
}6. 通过反射来干扰信号量从而破坏单例
只要通过反射来干扰信号量就可以继续破坏单例模式了见 main() 代码
package singletons;import java.lang.reflect.Constructor;
import java.lang.reflect.Field;/*** Author Fwy^ ^* Date*/
public class TripleSingleton3 {private static TripleSingleton3 INSTANCE null;// 再加一个信号量防止破坏单例private static boolean flag false;private TripleSingleton3(){// 再加一重锁防止破坏单例synchronized (TripleSingleton3.class){if(flag false){flag true;}else {throw new RuntimeException(不要通过反射来破坏单例模式);}}}// 三重校验锁public static TripleSingleton3 getInstance(){if(INSTANCE null){synchronized (TripleSingleton3.class){if(INSTANCE null){INSTANCE new TripleSingleton3();}}}return INSTANCE;}public static void main(String[] args) throws Exception{// 使用反射来干扰信号量Field flag TripleSingleton3.class.getDeclaredField(flag);flag.setAccessible(true);// 使用反射来破坏单例模式null 代表无参构造器ConstructorTripleSingleton3 constructor TripleSingleton3.class.getDeclaredConstructor(null);// 无视私有破坏单例模式constructor.setAccessible(true);TripleSingleton3 instance1 constructor.newInstance();flag.set(instance1, false);TripleSingleton3 instance2 constructor.newInstance();System.out.println(instance1 \n instance2);}
}7. 通过枚举类实现单例可以防止反射破坏单例
为了进行反射破坏先要获取 enum 类的构造器 观测源码发现有无参构造函数然而实际上这个是假的 。使用 javap 反编译会发现还是有无参构造函数使用 jad会找到一个 private EnumSingle(String s, int i) 的构造函数成功
public enum EnumSingleton {// 枚举实现单例可以防止通过反射破坏单例模式INSTANCE;public static void main(String[] args) throws Exception{System.out.println(EnumSingleton.INSTANCE);ConstructorEnumSingleton constructor EnumSingleton.class.getDeclaredConstructor(String.class, int.class);System.out.println(constructor.newInstance());}
}会爆出这个错误更加深入可以去看看 Enum 的源码