设计模式之单例模式最佳实现方式 (3)

在多线程中使用DCL懒汉式,8个线程只有一个线程能使用该类的构造方法,说明DCL懒汉式单例是线程安全的

public class DCLLazyMan {

   private DCLLazyMan(){
       System.out.println("DCL懒汉式单例 " + Thread.currentThread().getName());
  }

   private volatile static DCLLazyMan lazyMan;

   public static DCLLazyMan getInstance(){
       if (lazyMan == null){
           synchronized (DCLLazyMan.class){
               if (lazyMan == null){
                   lazyMan = new DCLLazyMan();
              }
          }
      }
       return lazyMan;
  }

   public static void main(String[] args) {
       for (int i = 0; i <9 ; i++) {
           new Thread(()->{
               DCLLazyMan.getInstance();
          }).start();
      }
  }
}

//输出结果:DCL懒汉式单例 Thread-0

使用反射破解DCL懒汉式单例的唯一性

public static void main(String[] args) throws Exception {

   //创建对象1和2,通过DCLLazyMan.getInstance()方法
   DCLLazyMan instance1 = DCLLazyMan.getInstance();
   DCLLazyMan instance2 = DCLLazyMan.getInstance();

   //创建对象3,通过反射获得构造方法的newInstance()方法
   Constructor constructor = DCLLazyMan.class.getDeclaredConstructor(null);
   constructor.setAccessible(true);
   DCLLazyMan instance3 = (DCLLazyMan)constructor.newInstance();

   //输出结果:instance1和instance2相等吗? true
   System.out.println("instance1和instance2相等吗? " + (instance1 == instance2));
   //输出结果:instance2和instance3相等吗? false
   System.out.println("instance2和instance3相等吗? " + (instance2 == instance3));
}

/*
结果分析:
对象1和2,都是通过正常方式创建的,所以指向的都是同一个对象
对象2和3,一个通过正常方式创建,一个通过反射方式创建,已经不是同一个对象了

说明反射可以破坏DCL懒汉式单例的唯一性
*/
4、解决DCL懒汉式被反射破坏唯一性的问题 4.1、第一种方法:再加一重检查 //修改TCLLazyMan的构造方法为:
private TCLLazyMan(){
   //第三重加锁判断
   synchronized (TCLLazyMan.class){
       if (lazyMan != null){
           throw new RuntimeException("不要使用反射破坏类的唯一性");
      }
  }
}


//测试
public static void main(String[] args) throws Exception {

   //创建对象1,通过DCLLazyMan.getInstance()方法
   TCLLazyMan instance1 = TCLLazyMan.getInstance();

   //创建对象2,通过反射获得构造方法的newInstance()方法
   Constructor constructor = TCLLazyMan.class.getDeclaredConstructor(null);
   constructor.setAccessible(true);
   TCLLazyMan instance2 = (TCLLazyMan)constructor.newInstance();
}

/*
结果分析:
Exception in thread "main" java.lang.reflect.InvocationTargetException
Caused by: java.lang.RuntimeException: 不要使用反射破坏类的唯一性

说明三重检测确实可以防止在对象已经被实例化后,再通过反射来创建对象的实例化
*/

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpjjjp.html