饿汉式,DCL懒汉式
1、饿汉式单例
/** * 饿汉式单例 * @author it-小林 * @date 2021年07月05日 9:05 */ public class HungryPattern { //可能会浪费空间,开辟了空间,却没有使用 private HungryPattern(){ } private final static HungryPattern hungryPattern = new HungryPattern(); public final HungryPattern getInstance(){ return hungryPattern; } }
2、饿汉式单例
(1)存在多线程并发模式,也就是说多线程模式下,会有问题。
1 /** 2 * @author it-小林 3 * @date 2021年07月05日 9:11 4 */ 5 public class LazyPattern { 6 7 private LazyPattern() { 8 9 } 10 11 private static LazyPattern lazyPattern; 12 13 private static LazyPattern getInstance(){ 14 if(lazyPattern == null){ 15 lazyPattern = new LazyPattern(); 16 } 17 return lazyPattern; 18 } 19 20 //多线程开发 21 public static void main(String[] args) { 22 Thread thread1 = new Thread(){ 23 @Override 24 public void run() { 25 System.out.println(LazyPattern.getInstance()); 26 } 27 }; 28 thread1.start(); 29 Thread thread2 = new Thread(){ 30 @Override 31 public void run() { 32 System.out.println(LazyPattern.getInstance()); 33 } 34 }; 35 thread2.start(); 36 } 37 }
(2)双重检测锁模式的 懒汉式单例 DCL懒汉式
注意:synchronized 解决并发问题,但是因为lazyMan = new LazyMan();
不是原子性操作(可以分割,见代码注释),可能发生指令重排序的问题,通过volatil来解决。
-
Java 语言提供了 volatile和 synchronized 两个关键字来保证线程之间操作的有序性,volatile 是因为其本身包含“禁止指令重排序”的语义,synchronized 是由“一个变量在同一个时刻只允许一条线程对其进行 lock 操作”这条规则获得的,此规则决定了持有同一个对象锁的两个同步块只能串行执行;
- 原子性就是指该操作是不可再分的。不论是多核还是单核,具有原子性的量,同一时刻只能有一个线程来对它进行操作。简而言之,在整个操作过程中不会被线程调度器中断的操作,都可认为是原子性。比如 a = 1。
/** * @author it-小林 * @date 2021年07月05日 9:11 */ public class LazyPattern { private LazyPattern() { } private static LazyPattern lazyPattern; //双重检测模式的 懒汉式单例 DCL懒汉式 private static LazyPattern getInstance(){ if(lazyPattern == null){ synchronized (LazyPattern.class){ if(lazyPattern == null){ //不是一个原子性操作 lazyPattern = new LazyPattern(); /** * 1.分配内存空间 * 2、执行构造方法,初始化对象 * 3、把这个对象指向这个空间 */ } } } return lazyPattern; } //多线程开发 public static void main(String[] args) { Thread thread1 = new Thread(){ @Override public void run() { System.out.println(LazyPattern.getInstance()); } }; thread1.start(); Thread thread2 = new Thread(){ @Override public void run() { System.out.println(LazyPattern.getInstance()); } }; thread2.start(); } }
3、静态内部类
1 /** 2 * 静态内部类 3 * @author it-小林 4 * @date 2021年07月05日 19:29 5 */ 6 public class Holder { 7 8 //构造器私有 9 public Holder() { 10 11 } 12 13 public static Holder getInstance(){ 14 return InnerClass.HOLDER; 15 } 16 17 public static class InnerClass{ 18 private static final Holder HOLDER = new Holder(); 19 } 20 21 }
4、单例不安全,反射破坏(见注释及main方法中反射破解步骤)
1 /** 2 * @author 林瑞涛 3 * @date 2021年07月05日 9:11 4 */ 5 public class LazyPattern { 6 7 //红绿等解决通过反射创建对象(反编译可以破解该方法) 8 private static boolean bolNew = false; 9 private LazyPattern() { 10 synchronized (LazyPattern.class){ 11 if(bolNew == false){ 12 bolNew = true; 13 }else{ 14 throw new RuntimeException("不要试图使用反射破坏单例"); 15 } 16 } 17 } 18 19 //volatile避免指令重排 20 private volatile static LazyPattern lazyPattern; 21 22 23 //双重检测模式的 懒汉式单例 DCL懒汉式 24 private static LazyPattern getInstance(){ 25 26 if(lazyPattern == null){ 27 //不是一个原子性操作 28 lazyPattern = new LazyPattern(); 29 } 30 return lazyPattern; 31 } 32 33 //反射 34 public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { 35 Field bolNew = LazyPattern.class.getDeclaredField("bolNew"); 36 bolNew.setAccessible(true); 37 38 Constructor<LazyPattern> declaredConstructor = LazyPattern.class.getDeclaredConstructor(null); 39 declaredConstructor.setAccessible(true);//无视私有的构造器 40 LazyPattern instance1 = declaredConstructor.newInstance(); 41 bolNew.set(instance1,false); 42 System.out.println(instance1); 43 LazyPattern instance2 = declaredConstructor.newInstance(); 44 45 System.out.println(instance2); 46 } 47 }
枚举:通过反射破解枚举发现不成功:
1、普通的反编译会欺骗开发者,说enum枚举是无参构造
2、实际enum为有参构造(见后面);
3、通过反射破解枚举会发现抛出异常
Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects at java.lang.reflect.Constructor.newInstance(Constructor.java:417) at com.ph.single.Test.main(EnumSingle.java:19)
1 //enmu是什么?本身也是一个class类 2 public enum EnumSingle { 3 INSTANCE; 4 public EnumSingle getInstance(){ 5 return INSTANCE; 6 } 7 } 8 9 class Test{ 10 public static void main(String[] args) throws Exception { 11 EnumSingle instance = EnumSingle.INSTANCE; 12 Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class); 13 declaredConstructor.setAccessible(true); 14 EnumSingle instance2 = declaredConstructor.newInstance(); 15 16 //java.lang.NoSuchMethodException: com.ph.single.EnumSingle.<init>() 17 System.out.println(instance); 18 System.out.println(instance2); 19 20 } 21 }
通过idea和jdk自带的反编译枚举如下:
通过JDK反编译枚举的代码如下, 发现枚举其实是有参构造