java 单例 dcl_单例模式DCL式以及Java指令无序性解析

这两天有点咸鱼,手头没项目写,今天翻了翻刘望舒大神的安卓进阶之光,刚好看到对volatile关键字的解析。自己以前很少涉及到关键字,直到它的存在但是没有了解过,今晚花了点时间把它搞清楚,就看到了单例模式中双重检查模式的应用。这里借助单例模式的双重检查模式讲讲。写写自己的一点小看法。

单例模式的目的是在程序执行期间,对实现单例模式的类,只存在唯一的一个实例。主要是针对某些创建销毁时特别吃内存的类或者想要保持数据同步的类。

但单例模式,很多时候碰到了多线程的问题,于是可爱的程序猿们想出了各种方法,包括这里说的这种双重检查模式DCL。先贴代码

e83879ada427751db5a769bf40688757.png

这里先提一提synchronized关键字,这个关键字是指同步代码块,即使用类或对象(在这里是Singleton.class)对花括号内的代码块进行同步,保证这个代码块在任何时间点最多只能有一个线程在执行。

那么这么写的目的是什么呢?看看如果不这么写,有什么不好的地方,同样贴代码

b253bed3073168738d1c41447d6a8c67.png

这是最懒的懒汉式。线程完全不安全。为什么呢?因为如果多个线程都调用了getInstance()这个方法,那么根据JVM的线程分配,可能会出现,第一个线程执行过if判断句,便让出时间片,第二个线程同样判断if语句,这时两个线程都进入了if语句块中,那么会new出两个实例,单例模式fail。那么为了解决这个问题,最初的思路就是使用synchronized这个关键字,对这个getInstance()进行同步(synchronized可以修饰方法,使方法同步),这个方法可行,但是这会降低执行效率,因为每次调用getInstance,都要进行同步锁的获取以及判定。那么我们自然会想,那就同步代码块就好了,于是有了这一种。

22036981b668c4cb83c8870549643111.png

或者这一种

f6f4c6589b192d7f3fdf8e7c784d6754.png

看起来很有道理,仔细想想,觉得事情还是不太对。先说第一种,第一种很有可能两个线程和之前一样,判断了instance==null,都进入了if块,那岂不是一样,那就只是new的时候保证了同步而已,其实还是没有卵用。第二种,行吧,倒是解决了问题,但是那岂不是跟把synchronized放在方法一样,每次进来都要做判断。于是我们想了想办法,把这两种集合了一下,变成了……这样!

看起来就很有道理,既实现了创建对象时的同步,又很大程度上解决了synchronized每次都要执行的效率。然而,还是有问题。这里就涉及到Java的一个特性。

为了实现“一次编译处处执行”的承诺,Java使用了虚拟机JVM的概念,将计算机真正的硬件进行了封装,提供了虚拟机这个概念。其中会有虚拟的寄存器、内存、处理器等等一系列虚拟概念。同时为了保证其代码的执行效率,JVM有一套处理器的指令执行算法,它允许在不改变程序结果的条件下,对指令执行的顺序进行调换,称为重排序。这在单线程的情况下没毛病,但是一旦涉及多线程操作,就会出现各种无法预知的错误。比如说,这里涉及的对象的创建。

JVM中对象的创建涉及多个指令,比如分配内存、堆内存中对象的初始化、返回堆内存中的引用等等。由于JVM的处理器执行指令的无序,创建对象时很有可能先在堆内存中开辟了一个空间,然后把它返回,然后再进行初始化。那这和我们说的这个单例模式有什么关系呢?设想这样一个情况。线程1已经进入了synchronized块中,并返回了对象的引用,但是初始化只执行了一半,这个时候,线程2到达了第一个if判断句中,它一看,哇,不null欸。直接返回了instance。这岂不是gg?我们拿到了一个初始化一半的instance,它要是一个人,那有可能少了一条腿。

那怎么解决呢?秘密武器来了—volatile!这个关键字用于修饰一个变量,使这个变量具有可见性、有序性(不保证原子性)。我们这里只讲有序性,不展开讲原子性和可见性。有序性就是用于克服JVM的指令无序性,它使得指令按照它原本的顺序执行,比如对象的创建,它会乖乖的,开辟内存,初始化,返回引用;于是代码变成了这样

这就是我们一开始贴出来的代码了。非常好的双重检查模式。

PS:关于单例模式还有很多种写法,个人认为这个写法并不是最好的,实现起来不顺手(也许是个人原因),而且据说新版JVM已经对这个问题已经进行了优化。单例模式完全可以写成饿汉或者登记式(内部类),也许会更好。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值