美文网首页
DCL单例模式与Volatile以及对象创建过程

DCL单例模式与Volatile以及对象创建过程

作者: 莱布尼兹的拉格朗日 | 来源:发表于2020-08-30 11:27 被阅读0次

    单例模式是最常见的一种设计模式,他的意思就是一个类只能创建一个实例对象出来。单例模式又分为懒汉式和饿汉式,第一个为这两种单例模式起名字的人是怎么想出来这个名字的。。。不过也挺好理解的,没有故意起一些听上去很厉害的名词。

    饿汉式单例模式代码如下:

    饿汉式单例

    饿汉式单例其实很简单,直接在类定义一个私有的静态实例就好了,这样一来在类加载的初始化的时候jvm就是执行<clinit>方法创建一个实例,jvm保证<clinit>方法只会被执行一次。所以是不会存在线程安全的问题的。但是有人说我都还没用呢就已经创建出来了,能不能在使用到的时候在创建呢。于是懒汉式就应运而生。懒汉懒汉就是这个类非常懒,只有你向他要对象的时候才给你。

    懒汉式第一版:

    根据饿汉式改进的似乎应该是这个样子的,但是,在单线程的情况下是没有任何问题的。如果是多线程呢,很显然并不能保证单例。那怎么办呢直接加锁效率比较低,于是就出现了DCL(double check lock)单例也就是双重检查锁。

    这种双重检测锁在多个线程都判断对象为空时,即使他们争取到了锁也要再次判断对象是否为空,保证了单例。这样看起来非常好既满足了单例,还保证了线程安全。

    但是在在创建对象的时候会有指令重拍的问题出现,还是有可能出现问题,尽管可能性非常小。

    创建对象全过程

    java创建对象时分为很多步的,如果按照这个顺序来一起都很顺利。但是cpu为了提高执行效率会产生指令重排序比如上面的(6)放在了(4)后面执行,这样在单线程下由于是串行执行的不会发生什么问题。但在DCL多线程的时候就有可能发生问题。

    指令重排的问题

    thread1在执行到第六步的时候已经将对象的引用赋给了singleten,那么singleton就不为null了,但是在这个时候thread2执行的时候发现singleton不为空就拿到singleton兑现直接使用了,但是此时的成员变量还只是初始值,并不是真正的值。如果thread2在调用成员变量时thread1已经执行了<init>方法,成员变量已经是正确值了,不会有数据不一致问题,但是thread1在thread2执行<init>方法之前调用了成员变量呢,这样数据就有问题了。

    此时,volatile就可以发挥作用了,volatile有两个作用保证线程可见性和禁止指令重排序,我们只需要使用volatile不让程序发生指令重排序就完美解决问题。

    最终版DCL单例

    这样就保证了只有在对象完全创建的时候,别的线程才能拿到该对象,就不会出现调用成员变量时数据不正确的问题了。

    懒汉式单例出现了这么多问题,可见一定不要懒。

    相关文章

      网友评论

          本文标题:DCL单例模式与Volatile以及对象创建过程

          本文链接:https://www.haomeiwen.com/subject/ctcgdktx.html