单例模式

参考 http://cantellow.iteye.com/blog/838473 ;公众号:程序员小灰
  1. 懒汉式
    1. 线程不安全:
  1. 这种写法lazy loading很明显,但是致命的是在多线程不能正常工作。需要的时候才会实例化
线程安全:
  1. 对比上一中,就多个Synchronized关键字,这种写法能够在多线程中很好的工作,而且看起来它也具备很好的lazy loading,但是,遗憾的是,效率很低,99%情况下不需要同步。
饿汉:
  1. 这种方式基于classloder机制,在深度分析Java的ClassLoader机制(源码级别)和Java类的加载、链接和初始化两个文章中有关于CLassload而机制的线程安全问题的介绍,避免了多线程的同步问题,不过,instance在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用getInstance方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance显然没有达到lazy loading的效果。
  2. 饿汉与懒汉:饿汉就是自己找东西,自己实例化好了,懒汉就是需要别人来喂,别人来实例化
双重校验锁:
  1. 其实这是种仍然不是绝对的线程安全
  2. 假设这样的场景,当两个线程一先一后访问getInstance方法的时候,当A线程正在构建对象,B线程刚刚进入方法:
  1. 这种情况表面看似没什么问题,要么Instance还没被线程A构建,线程B执行 if(instance == null)的时候得到true;要么Instance已经被线程A构建完成,线程B执行 if(instance == null)的时候得到false。真的如此吗?答案是否定的。这里涉及到了JVM编译器的指令重排。指令重排是什么意思呢?比如java中简单的一句 instance = new Singleton,会被编译器编译成如下JVM指令:
  1. 但是这些指令顺序并非一成不变,有可能会经过JVM和CPU的优化,指令重排成下面的顺序:
  1. 当线程A执行完1,3,时,instance对象还未完成初始化,但已经不再指向null。此时如果线程B抢占到CPU资源,执行  if(instance == null)的结果会是false,从而返回一个没有初始化完成的instance对象。如下图所示:

  1. 改良后:
  1. 两者区别,也就是多了个Volatile关键字
  2. 经过volatile的修饰,当线程A执行instance = new Singleton的时候,JVM执行顺序是什么样?始终保证是下面的顺序:
  1. 如此在线程B看来,instance对象的引用要么指向null,要么指向一个初始化完毕的Instance,而不会出现某个中间态,保证了安全。
静态内部类:
  1. .从外部无法访问静态内部类LazyHolder,只有当调用Singleton.getInstance方法的时候,才能得到单例对象INSTANCE。
  2. .INSTANCE对象初始化的时机并不是在单例类Singleton被加载的时候,而是在调用getInstance方法,使得静态内部类LazyHolder被加载的时候。因此这种实现方式是利用classloader的加载机制来实现懒加载,并保证构建单例的线程安全。
上面的四种方法都是无法防止利用反射来重复构建对象的用反射打破单例:
  1. 代码可以简单归纳为三个步骤:
    1. 第一步,获得单例类的构造器。
    2. 第二步,把构造器设置为可访问。
    3. 第三步,使用newInstance方法构造对象。
  2. 最后为了确认这两个对象是否真的是不同的对象,我们使用equals方法进行比较。毫无疑问,比较结果是false。
枚举:
  1. 为了检测是否能通过反射来打破通过枚举产生的单例:
  1. 然后出现
  1. 总结:

  1.  volatile关键字不但可以防止指令重排,也可以保证线程访问的变量值是主内存中的最新值。有关volatile的详细原理,我在以后的漫画中会专门讲解。
  2. .使用枚举实现的单例模式,不但可以防止利用反射强行构建单例对象,而且可以在枚举类对象被反序列化的时候,保证反序列的返回结果是同一对象。
  3. 对于其他方式实现的单例模式,如果既想要做到可序列化,又想要反序列化为同一对象,则必须实现readResolve方法。

github:https://github.com/wk1995/DesignPattern

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值