public class SingletonDemo { private static volatile SingletonDemo instance = null; private SingletonDemo(){ System.out.println(Thread.currentThread().getName() + " 我是构造方法SingletonDemo()"); } /* public static SingletonDemo getInstance(){ if (instance == null){ instance = new SingletonDemo(); } return instance; }*/ //DCL 双端检锁机制不一定是线程安全的,原因是有指令重排序的存在,加入volatile可以禁止指令重排 //原因在于某一个线程执行到第一次检索,读取到的instance不为null时,instance的引用对象可能没有完成初始化。 //指令重排只保证串行语句的执行一致性(单线程),但不会关心多线程间的语义一致性。 //所以当一条线程访问instance不为null,由于instance实例初始化未必完成,也就造成了线程安全问题。 public static SingletonDemo getInstance(){ if (instance == null){ synchronized (SingletonDemo.class){ if (instance == null){ instance = new SingletonDemo(); } } } return instance; } // 单例模式 :构造器私有化,static修饰,初始化用getInstance; public static void main(String[] args){ /*//单线程模式没有问题,只会初始化一次 System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance()); System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance()); System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance()); */ //多线程,情况发生变化;出现构造方法不止一次; // 解决方法一:public synchronized static SingletonDemo getInstance() // 解决方法二:DCL double check lock 双端检锁机制(锁之前检查并且锁之后检查) // 最优终极解决方案: DCL + volatile(禁止重排) // 原因: 方法一性能不好;方法二有0.0000001的失败率,失败的原因是多线程下编译器指令重排; // instance = new SingletonDemo(); :三个不存在数据依赖指令 1.分配内存空间 2 实例化对象 3 将分配的内存空间地址给instance; // if (instance == null){instance = new SingletonDemo();} 线程一完成了1,3 ,此时instance没有实例化,依然为null;线程3进去if语句,进行实例化; // 解决方法 禁止指令重排volatile; for (int i = 0; i<=100;i++){ Thread aa = new Thread() { @Override public void run() { SingletonDemo.getInstance(); }; }; aa.setName(String.valueOf(i)); aa.start(); } } }
singleton 单线程多线程 单例模式
最新推荐文章于 2021-06-29 18:34:27 发布