博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
单例双重加锁
阅读量:5067 次
发布时间:2019-06-12

本文共 1794 字,大约阅读时间需要 5 分钟。

  相信面向对象程序员都对单例模式比较熟悉,而对于单例模式却有着各种各样的写法,今天我专门针对一种称为双重加锁的写法进行分析。我们先来看下这种写法。

/** * 单例双重加锁Demo * */public class DoubleCheckLock {       private static DoubleCheckLock instance ;        private DoubleCheckLock(){            }        public static DoubleCheckLock getInstance(){        if(instance == null){            synchronized (DoubleCheckLock.class) {                if(instance == null)                    instance = new DoubleCheckLock() ;            }        }        return instance;    }}

  这种写法相信很多人都见过,但是你认为这种写法是正确的吗?或者更准确的来说,这种写法在并发的环境下是否还能表现出正确的行为呢。

  之所以有这种所谓的双重加锁,一方面是因为延迟初始化可以提高性能,另一方面通过使用内置锁sychronized来防止并发,其原理是首先检查是否在没有同步的情况下进行了初始化,如果没有的话,在进行同步,然后再次检查是否对其(instance)进行了初始化,如果没有那么则初始化DoubleCheckLock。

  这种写法表面看起来既提高了性能,又保证了线程安全。但实际上却并不是如此,我只从线程安全上来分析这种写法的对错。

  在这,首先应该注意的是使用内置锁加锁的是DoubleCheckLock.class,并不是instance,也就是说没有在instance实现同步,那么在这种情况下,当有两个线程同时进行到synchronized代码块时,只有一个线程可以进入,然后初始化了instance,但是这仅仅只能保证的是两个线程在访问上的独占性,也就是说两个线程在此一定是一先一后进行访问,但是不能保证的是instance的内存可见性,原因很简单,因为同步的对象并不是instance,而是DoubleCheckLock.class(可以保证内存可见性)。不能保证内存可见性的后果就是当第一个线程初始化instance之后,第二个线程并不能马上看见instance被初始化,或者更准确的来说,第二个线程看到的可能只是被部分构造的instance。因此,这种造成的后果是第二个线程读取到了错误的instance的状态,有可能instance会被再次实例化。

  那么如何解决这个问题呢,最简单的方式是对instance加上关键词volatile,volatile可以保证变量的内存可见性,同时volatile同步的消耗也非常小,这么做到话,可以保证线程安全。

  上述解决问题的方式固然是可以,但是实质上我感觉很繁琐其代码阅读效果也不好,就单例而言,我推荐一下的写法。

public class Single {        private Single(){}        private static class SingleHolder{        public static Single instance = new Single();    }        public static Single getInstance(){        return SingleHolder.instance;    }}

  这种写法相对而言比较简单,而且处理了两个问题:1.线程安全问题。2.延迟初始化(初始化在调用getInstance的时候才会去静态内部类中初始化instance)。而且相对而言,有着更加良好的代码可读性。

  对于双重加锁的这种写法就先分析到这,等后面说到Happens-Before之后我会再来分下下双重加锁。

 

转载于:https://www.cnblogs.com/yanfengfree/p/6271359.html

你可能感兴趣的文章
Recipe 1.4. Reversing a String by Words or Characters
查看>>
Rule 1: Make Fewer HTTP Requests(Chapter 1 of High performance Web Sites)
查看>>
sql注入
查看>>
「破解」Xposed强
查看>>
src与href的区别
查看>>
ABAP工作区,内表,标题行的定义和区别
查看>>
《xxx重大需求征集系统的》可用性和可修改性战术分析
查看>>
Python 中 创建类方法为什么要加self
查看>>
关于indexOf的使用
查看>>
【转】JS生成 UUID的四种方法
查看>>
英语单词
查看>>
centos6.8下安装matlab2009(图片转帖)
查看>>
Mongo自动备份
查看>>
求助大神!怎样批量删除数据库表中某个字段中同样的一段字符!
查看>>
VMWARE虚拟机无法访问的三种方法分析
查看>>
enq: SQ - contention
查看>>
cer证书签名验证
查看>>
ant 安装
查看>>
新手Python第一天(接触)
查看>>
iOS中ARC内部原理
查看>>