Java并发(七):双重检验锁定DCL
双重检查锁定(Double Check Lock,DCL)
1、懒汉式单例模式,无法保证线程安全:
public class Singleton {
private static Singleton singleton;
private Singleton() {
}
public static Singleton getInstance() {
if (singleton == null) {// 多个线程同时执行到此,会生成多个Singleton实例
singleton = new Singleton();
}
return singleton;
}
}
2、同步处理,synchronized就会导致这个方法比较低效:
public class Singleton {
private static Singleton singleton;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
3、双重检查 DCL:
public class Singleton {
private static Singleton singleton;
Integer a;
private Singleton(){}
public static Singleton getInstance(){
if(singleton == null){ // 1 只有singleton==null时才加锁,性能好
synchronized (Singleton.class){ //
if(singleton == null){ //
singleton = new Singleton(); //
}
}
}
return singleton;
}
}
但是,仍然有问题!!
创建对象过程:
(1)分配内存空间
(2)初始化对象
(3)将内存空间的地址赋值给对应的引用
(2)(3)会被处理器优化,发生重排序
举例:
A线程singleton = new Singleton()发生重排序,将分配的内存空间引用赋值给了静态属性singleton(即singleton != null),而对象还未初始化(即Integer a == null);
B线程此时调用getInstance()方法,因为singleton != null,直接返回singleton。当B线程使用singleton的a属性时就会空指针。

分析:
问题在于singleton = new Singleton()的重排序
(1)不允许初始化阶段步骤2 、3发生重排序。
(2)允许初始化阶段步骤2 、3发生重排序,但是不允许其他线程“看到”这个重排序。
解决:
1、利用volatile限制重排序
public class Singleton {
private volatile static Singleton singleton;// 通过volatile关键字来确保安全
private Singleton(){}
public static Singleton getInstance(){
if(singleton == null){
synchronized (Singleton.class){
if(singleton == null){
singleton = new Singleton();
}
}
}
return singleton;
}
}
(1)分配内存空间
(2)初始化对象
(3)将内存空间的地址赋值给对应的引用
第(3)步 volatile修饰的变量singleton的写入操作,通过内存屏障限制的重排序 参考:Java并发(六):volatile的实现原理
2、利用类初始化
JVM会保证一个类的类构造器在多线程环境中被正确的加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的类构造器,其他线程都需要阻塞等待,直到活动线程执行方法完毕。
特别需要注意的是,在这种情形下,其他线程虽然会被阻塞,但如果执行初始化的那条线程退出后,其他线程在唤醒之后不会再次进入/执行初始化,因为在同一个类加载器下,一个类型只会被初始化一次。
public class Singleton {
private static class SingletonHolder{
public static Singleton singleton = new Singleton();
}
public static Singleton getInstance(){
return SingletonHolder.singleton;
}
}
参考资料:
【死磕Java并发】—–Java内存模型之从JMM角度分析DCL
Java并发(七):双重检验锁定DCL的更多相关文章
- Java盲点:双重检查锁定及单例模式
尊重原创: http://gstarwd.iteye.com/blog/692937 2004 年 5 月 01 日 所有的编程语言都有一些共用的习语.了解和使用一些习语很有用,程序员们花费宝贵的时间 ...
- JAVA并发七(多线程环境中安全使用集合API)
在集合API中,最初设计的Vector和Hashtable是多线程安全的.例如:对于Vector来说,用来添加和删除元素的方法是同步的.如果只有一个线程与Vector的实例交互,那么,要求获取和释放对 ...
- java 并发(七)--- ThreadLocal
文章部分图片来自参考资料 问题 : ThreadLocal 底层原理 ThreadLocal 需要注意什么问题,造成问题的原因是什么,防护措施是什么 ThreadLocal 概述 Threa ...
- Java并发--三大性质
一.多线程的三大性质 原子性:可见性.有序性 二.原子性 原子性介绍 原子性是指:一个操作时不可能中断的,要么全部执行成功要么全部执行失败,有着同生共死的感觉.即使在多线程一起执行的时候,一个操作一旦 ...
- java并发编程系列七:volatile和sinchronized底层实现原理
一.线程安全 1. 怎样让多线程下的类安全起来 无状态.加锁.让类不可变.栈封闭.安全的发布对象 2. 死锁 2.1 死锁概念及解决死锁的原则 一定发生在多个线程争夺多个资源里的情况下,发生的原因是 ...
- DCL,即Double Check Lock,中卫双重检查锁定。
DCL,即Double Check Lock,中卫双重检查锁定. [Java并发编程]之十六:深入Java内存模型——happen-before规则及其对DCL的分析(含代码) 关于单例.关于DCL: ...
- java中的双重锁定检查(Double Check Lock)
原文:http://www.infoq.com/cn/articles/double-checked-locking-with-delay-initialization#theCommentsSect ...
- Java并发——DCL问题
转自:http://www.iteye.com/topic/875420 如果你搜索网上分析dcl为什么在java中失效的原因,都会谈到编译器会做优化云云,我相信大家看到这个一定会觉得很沮丧.很无助, ...
- JAVA 双重检查锁定和延迟初始化
双重检查锁定的由来在Java程序中,有时需要推迟一些高开销的对象的初始化操作,并且只有在真正使用到这个对象的时候,才进行初始化,此时,就需要延迟初始化技术.延迟初始化的正确实现是需要一些技巧的,否则容 ...
随机推荐
- 【洛谷 P3965】 [TJOI2013]循环格(费用流)
题目链接 回路限制经典题. 每个点拆成入点和出点,源点连每个点的出点,流量1,费用0,每个点出点连汇点,流量1,费用0,入点和出点之间没有边. 也就是说每个点必须靠其他点流来的流量来流入汇点,同时自己 ...
- Python3 断言
#!/usr/bin/env python # _*_ coding:utf-8 _*_ # Author:CarsonLi ''' 断言一般用于后面有非常重要的操作,需要使用前面的数据,而且不容许出 ...
- 利用gcc的__attribute__编译属性section子项构建初始化函数表【转】
转自:https://my.oschina.net/u/180497/blog/177206 gcc的__attribute__编译属性有很多子项,用于改变作用对象的特性.这里讨论section子项的 ...
- shell编程===执行shell脚本的四种方法
使用vim创建一个shell文件,命名 hello.sh #!/bin/bash echo "hello shell !" 在linux中进行加载 chmod +x ./hello ...
- mysql视图学习总结(转)
一.使用视图的理由是什么?1.安全性.一般是这样做的:创建一个视图,定义好该视图所操作的数据.之后将用户权限与视图绑定.这样的方式是使用到 了一个特性:grant语句可以针对视图进行授予权限.2.查询 ...
- 数据库连接池(c3p0与druid)
1.数据库连接池概念 其实就是一个容器(集合),存放数据库连接的容器.当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归 ...
- java并发编程实战笔记---(第三章)对象的共享
3.1 可见性 synchronized 不仅实现了原子性操作或者确定了临界区,而且确保内存可见性. *****必须在同步中才能保证:当一个线程修改了对象状态之后,另一个线程可以看到发生的状态变化. ...
- TCP可靠传输和拥塞控制
1.TCP的可靠传输 tcp的可靠传输主要靠 来自接收方的确认报文 和 超时重传. 发出报文,计时器开始计时,在规定超时时间内未收到确认报文则重新发送. 注意:发送报文都留一个副本,如果收到确认报文就 ...
- Codeforces 798D - Mike and distribution(二维贪心、(玄学)随机排列)
题目链接:http://codeforces.com/problemset/problem/798/D 题目大意:从长度为n的序列A和序列B中分别选出k个下表相同的数要求,设这两个序列中k个数和分别为 ...
- Windows内核进程管理器解析
Windows内核是如何实现线程挂起的?如何实现线程挂载到进程的?如何实现杀死进程和线程的? 从源码分析一下,这些操作具体在源码上是如何实现的. 进程创建.线程切换.线程跨越CPU权限级.进程挂靠.杀 ...