对于双检锁,其实有多种不同的用法,有很多种用法是无论如何不会出现问题的.我最初用双检锁来获取jndi对象时,立即有人告诉我双检锁是不安全的,我笑着告诉他:是否安全我比你更有把握.
static
DataSource ds = null;
public
static
DataSource getDataSource(){
if
(ds == null){
synchronized
(this
.getClass()){
if
(ds == null)
ds = xxx;
}
}
return
ds;
}
这样的DCL有什么安全问题呢?它仅仅是为了不做重复的劳动.一是ds本身是已经存在的对象,不是动态构造的,二是即使多次获取它也还是同一引用.这里做
的工作仅仅是不想让另一个线程多做在JNDI上再查找一次的工作,因为查找本身是耗时的,与其让另一个线程再查找还不如把它阻塞在
synchronized外面什么也不做.事实上即使是再查找一次两次获得的还是相同的引用,而且是已经构造好,不存在初始化问题的对象.DCL会有什么
安全问题?
所以不要一看到双检锁就认为它有安全问题.有些时候它完全可以非常好地工作.只是你要理解
它的安全问题到底在哪儿?
DCL到底有什么问题?
DCL的安全性
最
初被公开的时候在2001年JavaWorld.其实那个例子在那个时候已经是错误的.影响了后面几代人都跟着错误.有时候我甚至不敢相信一些非常简单的
问题因为放在权威的地方就没有人敢去责疑.最初的问题提出者在这儿:http://www.javaworld.com/javaworld/jw-
02-2001/jw-0209-double.html.例子是:
class
SomeClass {
private
Resource resource = null;
public
Resource getResource() {
if
(resource == null) {
synchronized
{
if
(resource == null)
resource = new
Resource();
}
}
return
resource;
}
}
我不知道提这个问题的人是否是个老古董.在2001年,JVM1.2已经发布好久了.JMM(Java Memory
Model)已经已经发布了新的规范,竟然在这个时候提出这样的问题.而且这个已经不存在的问题在此后的五年(今年是2006年)中一误导着很多人,甚至
是一些博士.
这个问题的提出者是这样说的.当一个线程运行到resource = new
Resource();时,因为new一个对象需要分配空间,初始化字段,调用构造方法.当为resource分配好空间后,外面的其它线程就可以看到不
为null的resource,而这时有可能还没有初始化字段和调用构造方法就被其它线程引用了.确实,在JAVA2(以jdk1.2开始)以前对于实例
字段是直接在主储区读写的.所以当一个线程对resource进行分配空间,初始化和调用构造方法时,可能在其它线程中分配空间动作可见了,而初始化和调
用构造方法还没有完成.
但是从JAVA2以后,JMM发生了根本的改变,分配空间,初始化,调用构造方法只会在线程的工作存储区完成,在没有向主存储区复制赋值时,其它线程绝对
不可能见到这个过程.而这个字段复制到主存区的过程,更不会有分配空间后没有初始化或没有调用构造方法的可能.在JAVA中,一切都是按引用的值复制的.
向主存储区同步其实就是把线程工作存储区的这个已经构造好的对象有压缩堆地址值COPY给主存储区的那个变量.这个过程对于其它线程,要么是
resource为null,要么是完整的对象.绝对不会把一个已经分配空间却没有构造好的对象让其它线程可见.
至于不同线程如何看到另一个线程对实例字段的改变,可以参看我的另一篇文章:深入
理解JMM.
那么上面的例子是否有问题?
我可以负责地说,在JVM1.2以后,上面的例子没有问题.(也许还是有问题但以我的水平还没有发现,但绝对不是JW上提出的那样的问题,那个问题只在
1.2以前出现,而2001年早已不是jdk1.1的时代了),很多时候我们需要有独立的思考,当你觉得你获取一个新知识点时你一定要弄清它存在的环境.
那么到底DCL是否真的就没有问题了呢?否,现在的问题还是可见性问题,但问题转到了同一对象不同字段上面,这个问题已经在以前说过了:
public
MyObject{
private
static
MyObect obj;
private
Date d = new
Data();
public
Data getD(){return
this
.d;}
public
static
MyObect getInstance(){
if
(obj == null){
synchronized
(MyObect .class
){
if
(obj == null)
obj = new
MyObject();//这里
}
}
return
obj;
}
}
一个线程A运行到"这里"时,对于A的工作区中,肯定已经产生一个MyObect对象,而且这时这个对象已经完成了Data
d.现在线程A调用时间到,执行权被切换到另一个线程B来执行,会有什么问题呢?如果obj不为null,线程B获得了一个obj,但可能
obj.getD()却还没有初始化.为什么?既然obj已经可见了(线程A还没有离开同步块),而d却不可见呢?如果d不可见,那么obj也应该为
null啊?线程B应该等待A释放同步块啊?事实上,对于"这里"这条语句,线程A还没有离开同步块.因为没有"离开同步块"这个条件,线程a的工作区没
有强制与主存储器同步,这时工作区中有两个字段obj,d
到底先把谁同步到主存储区,没有条件限制,虽然在线程A的工作区obj和d都是完整的,但有JSL没有强制不允许先把obj映射到主存储区,如果哪个
jvm实现按它的优化方案先把工作存储器中的obj同步到主存储器了,这时正好线程B获取了,而d却没有同步过去,那么线程B就获取了obj的引用却找不
能obj.getD();
我们发现,其实对于安全性问题都是基于即时构造对象这样的条件下的.如果你把DCL用来控制其它不重复操作,它就不会出现这样的问题,就象上面从JNDI
中查找DataSource,因为查找本身是耗时的,所以我用DCL来控制"查找"这个行为而不是控制对象本身.其实还有很多可以正确应用DCL的地方.
象JLive,OFBiz这些开源项目中都在大量应用DCL,当然我不是推荐大家都去用DCL,而是如果需要而且合适就去用.有些问题可以用其它方法来代
替.只要你能真正明白它在什么时候是不安全的,你就可以安全地使用DCL.
from:
http://www.edu114.cn/java/java785333_2.html
分享到:
相关推荐
本实验主要目的是理解DCL中的GRANT和REVOKE语句如何用于权限管理,并在SQL Server 2000的查询分析器中实践这些操作。 一、实验目标 通过DCL,我们可以对数据库用户进行授权和撤销权限,以此来控制数据的存取。本次...
总的来说,这篇文章通过实例和分析,揭示了Java DCL模式的一些常见误解,并强调了深入理解JMM对于开发高效、可靠的并发代码的重要性。通过这种方式,开发者可以更好地掌握如何在多线程环境中正确地实现单例模式。
- **视图**:视图是基于一个或多个表的结果集的虚拟表,它可以简化复杂查询,同时提高数据的安全性。 - **触发器**:触发器是一种特殊类型的存储过程,它可以在特定事件发生时自动执行,如插入、更新或删除记录。 #...
4. **安全性**:认证、授权、加密、防火墙等安全机制,保护系统免受攻击。 5. **性能监控**:如何使用工具监测系统性能,及时发现并解决问题。 6. **设计原则**:例如单一职责原则、开闭原则、依赖倒置原则等,指导...
实验七《数据的安全性》是针对数据库原理课程的一个实践环节,旨在帮助学生深入理解数据安全性的重要性和实际操作方法。在这个实验中,主要涉及的是SQL Server数据库管理系统中的安全机制,包括四个层面的防御策略:...
Java单例模式是一种设计模式,它允许...以上就是关于Java单例模式的深入理解和常见实现方式,希望对你理解单例模式有所帮助。在实际开发中,灵活运用并结合具体场景选择合适的单例模式将有助于提高代码质量和可维护性。
在数据库管理系统(DBMS)中,数据控制语言(DCL)是一组用于管理和控制数据库安全性的SQL命令集合。本篇文章将深入探讨MySQL中的DCL权限控制操作,包括如何创建新用户、授予与撤销用户的权限等。掌握这些操作对于确保...
标题“PKWARE_DCL_fortyuht_zip_pkware_”提到了PKWARE Data Compression Library,这是一款由PKWARE公司开发的...通过深入理解和使用PKWARE DCL,开发者可以提升其项目的数据处理能力,同时确保数据的安全和兼容性。
通过DCL,数据库管理员可以有效地管理用户的登录、权限分配和撤销等操作,确保数据的安全性和完整性。本篇文章将详细介绍MySQL中的DCL用户管理操作,包括创建、修改、删除用户以及权限的授予与撤销。 #### 二、创建...
另外,线程安全的设计模式,如生产者-消费者模式、双检锁/双重校验锁(DCL)和懒汉式/饿汉式初始化,也是学习的重点。 最后,性能调优和并发问题的诊断也是高并发编程不可或缺的部分。如何通过JVM的监控工具(如...
而电磁炉的运行离不开精确且高效的控制程序,本篇文章将深入探讨“DCL.zip_cooker_炉控制_电磁炉_电磁炉程序”这一主题,主要涉及C语言和ASM(汇编语言)在电磁炉控制程序中的应用。 首先,我们来看C语言在电磁炉...
在选择和使用电磁炉时,消费者应关注其能效比、安全性、操作简便性等因素。46R12A型号的电磁炉,如果具备优秀的热效率和智能化控制,无疑会提供优质的烹饪体验。同时,了解电磁炉的工作原理和维护知识,能够帮助用户...
实验的主要目的是让学生深入理解数据安全性,并掌握SQL Server中关于用户、角色和权限管理的方法。以下是实验内容的详细阐述: 1. **Windows NT的网络安全**: - 在Windows NT环境中,网络管理员负责创建用户组,...
5. **并发设计模式**:书中涵盖了多种经典的并发设计模式,如生产者消费者模式、读写锁模式、双检锁/双重校验锁模式(DCL)等,帮助读者理解如何在实际项目中应用并发设计。 6. **线程池**:Java的`ExecutorService...
6. **安全性管理**:理解Oracle的角色、权限和对象权限,以及如何通过DBA账户管理用户访问,是保证数据库安全的关键。此外,了解审计功能和加密技术也是保障数据安全的重要部分。 7. **故障诊断与案例分析**:书中...
通过这四个实验,学生不仅掌握了SQL Server的基础操作,还对数据库设计原则和实践有了深入理解。实验过程中的每个步骤都是为了提升学生在实际工作中设计和管理数据库的能力。通过这些实践经验,学生将能够更好地应对...
在Java中,实现单例模式有多种方法,包括懒汉式(线程不安全)、饿汉式(静态常量)、双检锁(DCL)和枚举单例。其中,双检锁和枚举单例是线程安全的,推荐在多线程环境下使用。 ```java // 双检锁/双重校验锁(DCL...
8. **并发编程模式**:包括生产者-消费者模式、读者-写者模式、双检锁/双重校验锁(DCL)、管程等,这些模式在解决并发问题时有着广泛应用。 9. **线程安全**:理解什么是线程安全的类和方法,以及如何编写线程安全...
5. **高性能设计模式**:包括生产者消费者模型、读写锁策略、双检锁/双重校验锁(DCL)、线程局部变量(ThreadLocal)等,这些都是解决高并发问题时常用的设计模式。 6. **JVM内存模型与并发**:Java内存模型(JMM)...
1. 存储过程:预编译的SQL语句集合,可以封装复杂的业务逻辑,提高代码复用和安全性。 2. 触发器:在特定数据库事件(如INSERT、UPDATE、DELETE)发生时自动执行的代码,常用于实现业务规则。 通过深入学习这些SQL...