单例模式以及双检锁DCL
来源:互联网 发布:关于网络女主播电影 编辑:程序博客网 时间:2024/05/21 14:41
回想起曾经经历的一次面试,是关于设计模式的。当时自己讲了单例模式,但是并没有全面地把与单例模式相关的线程安全问题理解清楚,当时的场景,哎,往事不堪回首,不过技术终归于技术,是必须掌握滴~
1、单例模式
关于单例模式,就不再详细叙述,想必大家都耳熟能详了,简单回顾下吧。以下是单例模式的一个例子:
上述的例子,如果是在并发的情况下,就会遇到严重的问题。比如线程A在判断instance为空时,进入new操作,new操作还未完成时,此时线程B也运行到判断instance是否为NULL,那么可能就会造成线程A和线程B都在new,那就违背了单例模式的原本含义了。那么既然需要保证只有一个实例,我们是否可以通过synchronized关键字来解决呢?
不可否认,synchronized关键字是可以保证单例,但是程序的性能却不容乐观,原因在于getInstance()整个方法体都是同步的,这就限定了访问速度。其实我们需要的仅仅是在首次初始化对象的时候需要同步,对于之后的获取不需要同步锁。因此,可以做进一步的改进:
这样我们将上锁的粒度降低到了仅仅是初始化实例的那部分,从而使代码即正确又保证了执行效率。这就是所谓的“双检锁”机制(顾名思义)。
双检锁机制的出现确实是解决了多线程并行中不会出现重复new对象,而且也实现了懒加载,但是很可惜,这样的写法在很多平台和优化编译器上是错误的,原因在于:instance=new DoubleCheckedLock()这行代码在不同编译器上的行为是无法预知的。一个优化编译器可以合法地如下实现 instance=new DoubleCheckedLock():
1. 给新的实体instance分配内存;
2. 调用DoubleCheckedLock的构造函数来初始化instance。
现在想象一下有线程A和B在调用DoubleCheckedLock,线程A先进入,在执行到步骤4的时候被踢出了cpu。然后线程B进入,B看到的是instance已经不是null了(内存已经分配),于是它开始放心地使用instance,但这个是错误的,因为A还没有来得及完成instance的初始化,而线程B就返回了未被初始化的instance实例。
当我们结合java虚拟机的类加载过程就会更好理解。对于JVM加载类过程,我还不是很熟悉,所以简要地介绍下:
jvm加载一个类大体分为三个步骤:
1)加载阶段:就是在硬盘上寻找java文件对应的class文件,并将class文件中的二进制数据加载到内存中,将其放在运行期数据区的方法区中去,然后在堆区创建一个java.lang.Class对象,用来封装在方法区内的数据结构;
2)连接阶段:这个阶段分为三个步骤,步骤一:验证,当然是验证这个class文件里面的二进制数据是否符合java规范;步骤二:准备,为该类的静态变量分配内存空间,并将变量赋一个默认值,比如int的默认值为0;步骤三:解析,这个阶段就不好解释了,将符号引用转化为直接引用,涉及到指针;
3)初始化阶段:当我们主动调用该类的时候,将该类的变量赋于正确的值(这里不要和第二阶段的准备混淆了),举个例子说明下两个区别,比如一个类里有private static int i = 5; 这个静态变量在"准备"阶段会被分配一个内存空间并且被赋予一个默认值0,当道到初始化阶段的时候会将这个变量赋予正确的值即5,了解了吧!
因此,双检锁对于基础类型(比如int)适用。因为基础类型没有调用构造函数这一步。那么对于双检锁中因编译器的优化无法保证执行顺序的问题,具体地说是在C++下是精简指令集(RISC)机器的编译器会重新排列编译器生成的汇编语言指令,从而使代码能够最佳运用RISC处理器的平行特性,因此有可能破坏双检锁模式。对于此问题,查阅了不少解决方案,主要有以下几种:
1)使用memory barrier,,关于merrory barrier的介绍,可参阅博文《Memory barrier》。
2)java中可考虑volatile关键字定义新的语意来解决这个问题,关于volatile关键字的使用,可见博文《volatile关键字》。
- 单例模式以及双检锁DCL
- 单例模式以及双检锁DCL
- 单例模式以及双检锁DCL
- 单例模式下的双检锁DCL
- 单例模式的DCL问题
- 单例模式(DCL缺陷以及如何安全发布对象)
- 双重检查模式 (DCL)与单例模式
- 单例模式 DCL延迟初始化的不足和改进
- 单例模式、双检测锁定DCL、volatile详解
- 单例模式、双检测锁定DCL、volatile(转)
- 单例模式、双检测锁定DCL、volatile
- 单例模式、双检测锁定DCL、volatile
- 单例模式、双检测锁定DCL、volatile(转)
- 单例模式、双检测锁定DCL、volatile(转)
- java多线程之单例模式和其DCL问题
- 设计模式 之 单例模式 (C++ 懒汉经典实现 & DCL实现 & 饿汉经典实现)
- 单例模式 (二) 延迟加载/"懒汉模式" —— 使用DCL双检查锁机制
- 杂谈Singleton模式,Monostate以及DCL
- [Python] 'unicode' object is not callable
- 基于Java的软件测试(下)
- LeetCode Ransom Note(字符串)
- sudo: /usr/bin/sudo must be owned by uid 0 and have the setuid bit set的解决办法
- 使用HorizontalScrollView 注意事项
- 单例模式以及双检锁DCL
- Linux电源管理(11)_Runtime PM之功能描述
- Android Studio的格式化快捷键ctrl+shift+F失效?
- Hdu 5861 Road
- MessageBox
- Android Data Binding基础用法
- 创建子类编译报错问题
- wuzhicms 来源管理
- mysql命令