原文地址:
http://zh.wikipedia.org/wiki/%E5%8F%8C%E9%87%8D%E6%A3%80%E6%9F%A5%E9%94%81%E5%AE%9A%E6%A8%A1%E5%BC%8F
双重检查锁定模式(也被称为"双重检查加锁优化","锁暗示"(Lock hint)[1]) 是一种软件设计模式用来减少并发系统中竞争和同步的开销。双重检查锁定模式首先验证锁定条件(第一次检查),只有通过锁定条件验证才真正的进行加锁逻辑并再次验证条件(第二次检查)。
该模式在某些语言在某些硬件平台的实现可能是不安全的。有的时候,这一模式被看做是反模式。
它通常用于减少加锁开销,尤其是为多线程环境中的单例模式实现“惰性初始化”。惰性初始化的意思是直到第一次访问时才初始化它的值。
考虑下面的Java代码
// Single threaded version
class Foo {
private Helper helper = null;
public Helper getHelper() {
if (helper == null) {
helper = new Helper();
}
return helper;
}
// other functions and members...
}
这段在使用多线程的情况下无法正常工作。在多个线程同时调用getHelper()时,必须要获取锁,否则,这些线程可能同时去创建对象,或者某个线程会得到一个未完全初始化的对象。
锁可以通过代价很高的同步来获得,就像下面的例子一样。
// Correct but possibly expensive multithreaded version
class Foo {
private Helper helper = null;
public synchronized Helper getHelper() {
if (helper == null) {
helper = new Helper();
}
return helper;
}
// other functions and members...
}
只有getHelper()的第一次调用需要同步创建对象,创建之后getHelper()只是简单的返回成员变量,而这里是无需同步的。 由于同步一个方法会降低100倍或更高的性能[2], 每次调用获取和释放锁的开销似乎是可以避免的:一旦初始化完成,获取和释放锁就显得很不必要。许多程序员一下面这种方式进行优化:
检查变量是否被初始化(不去获得锁),如果已被初始化立即返回这个变量。
获取锁
第二次检查变量是否已经被初始化:如果其他线程曾获取过锁,那么变量已被初始化,返回初始化的变量。
否则,初始化并返回变量。
// Broken multithreaded version
// "Double-Checked Locking" idiom
class Foo {
private Helper helper = null;
public Helper getHelper() {
if (helper == null) {
synchronized(this) {
if (helper == null) {
helper = new Helper();
}
}
}
return helper;
}
// other functions and members...
}
直觉上,这个算法看起来像是该问题的有效解决方案。然而,这一技术还有许多需要避免的细微问题。例如,考虑下面的事件序列:
线程A发现变量没有被初始化, 然后它获取锁并开始变量的初始化。
由于某些编程语言的语义,编译器生成的代码允许在线程A执行完变量的初始化之前,更新变量并将其指向部分初始化的对象。
线程B发现共享变量已经被初始化,并返回变量。由于线程B确信变量已被初始化,它没有获取锁。如果在A完成初始化之前共享变量对B可见(这是由于A没有完成初始化或者因为一些初始化的值还没有穿过B使用的内存(缓存一致性)),程序很可能会崩溃。
在J2SE 1.4或更早的版本中使用双重检查锁有潜在的危险,有时会正常工作:区分正确实现和有小问题的实现是很困难的。取决于编译器,线程的调度和其他并发系统活动,不正确的实现双重检查锁导致的异常结果可能会间歇性出现。重现异常是十分困难的。
在J2SE 5.0中,这一问题被修正了。volatile关键字保证多个线程可以正确处理单件实例。[4]描述了这一新的语言特性:
// Works with acquire/release semantics for volatile
// Broken under Java 1.4 and earlier semantics for volatile
class Foo {
private volatile Helper helper = null;
public Helper getHelper() {
Helper result = helper;
if (result == null) {
synchronized(this) {
result = helper;
if (result == null) {
helper = result = new Helper();
}
}
}
return result;
}
// other functions and members...
}
注意局部变量result的使用看起来是不必要的。对于某些版本的Java虚拟机,这会使代码提速25%,而对其他的版本则无关痛痒。[3]
如果helper对象是静态的(每个类只有一个), 可以使用双重检查锁的替代模式惰性初始化模式[4]。查看[5] 上的列表16.6。
// Correct lazy initialization in Java
@ThreadSafe
class Foo {
private static class HelperHolder {
public static Helper helper = new Helper();
}
public static Helper getHelper() {
return HelperHolder.helper;
}
}
这是因为内部类直到他们被引用时才会加载。
Java 5中的final语义可以不使用volatile关键字实现安全的创建对象:[6]
public class FinalWrapper<T> {
public final T value;
public FinalWrapper(T value) {
this.value = value;
}
}
public class Foo {
private FinalWrapper<Helper> helperWrapper = null;
public Helper getHelper() {
FinalWrapper<Helper> wrapper = helperWrapper;
if (wrapper == null) {
synchronized(this) {
if (helperWrapper == null) {
helperWrapper = new FinalWrapper<Helper>(new Helper());
}
wrapper = helperWrapper;
}
}
return wrapper.value;
}
}
为了正确性,局部变量wrapper是必须的。这一实现的性能不一定比使用volatile的性能更高。
分享到:
相关推荐
wikipedia-client, 维基百科API的ruby 客户端 维基百科 允许你通过他们的API获取维基百科内容。 它 将alpha API,不是过时的query.php API类型Wikipedia API参考:http://en.wikipedia.org/w/api.php来自:h
listen-to-wikipedia, 维基百科编辑中的实时生成音乐 听维基百科维基百科活动的实时可视化与巩固。由 LaPorte 和 Mahmoud Hashemi构建。听维基百科的灵感由 Maximillian 的laumeister BitListen 。我们用声音交换来...
UIColor-WikiColors 所有维基百科的颜色实现为易于使用的UIColor扩展
三角函数-自维基百科
DrQA 阅读维基百科的pytorch实现来回答开放领域的问题
数据可视化——来自(维基百科定义).pdf数据可视化——来自(维基百科定义).pdf
维基百科中文离线包zim格式,wikipedia_zh_all_maxi_2020-05.zim,使用kiwi打开
维基百科(Wikipedia)是规模最大的在线网络百科全书之一,采用群体在线合作编辑的Wiki机制,具有质量高、覆盖广、实时演化和半结构化等特点,是用来构建语义知识库的优质语料来源。分析了维基百科语料库的基本情况,...
提出一种基于维基百科的领域实体发现方法,该方法将构成领域实体的典型字或词作为种子元素,利用少量种子元素作为实体发现的初始知识,有效地克服了传统方法在获取种子词条时过分依赖领域专家的局限,同时还利用维基...
维基百科离线版 一个为了方便本地浏览维基百科查询资料而制作的 开源软件,程序的原理是利用维基百科 kiwix-tools 中的其中一个小工具:kiwix-serve 读取 .zim 格式的维基百科数据库文件,从而实现 http 方式多终端...
从任何维基百科稳重中导入表单,作为Python的数据集。
主要讲述的是正态分布的一些概念,还有他的历史,格式是PDF格式
美国二级三级行政区划、包括洲县,县城,县的归属州、成立时间、起源、人口、面积、编码、介绍链接、等信息。
http://zh.wikipedia.org/zh/Java
卡尔曼滤波-维基百科 卡尔曼滤波器在技术上有许多应用。常见的 应用是用于车辆,尤其是飞机和航天器的引 导,导航和控制。 [1] 此外,卡尔曼滤波器是 时间序列分析中广泛应用的概念,用于信号 处理和计量经济学等...
中文维基百科hosts文件,拷贝到C:\Windows\System32\drivers\etc目录下,经测试可使用
维基百科java实现API The jars in this package are NOT directly runnable. In order to run the jars, you have to add the jars in the dependency folder to your classpath and define the main class. These ...
维基百科词向量 sgns.wiki.char.bz2解压后文件后缀名是.char, 可以通过一些方法得到.txt结尾的文件,有35万多个字词和符号,300维的向量表示。将词向量作为词嵌入层时需要加载全部的词向量到内存,如果计算机的内存...
网址:http://en.wikipedia.org/wiki/Wikipedia 数据获得描述: 1.获取主页的内容,分析网页内容并找到主页上所有的本站链接
理解维基百科词条链接网络的结构特征是深入而有效地应用维基百科的前提。基于2010年1月的数据, 从度分布、权分布、宏观结构特征等角度对维基百科词条链接网络的结构特征展开实证分析。相关结果与2006年之前的维基...