其实不用太纠结什么“对象分配了引用,但还没创建完毕,另外一个线程发现引用不为null,使用报错“。 就按代码逻辑去理解, DCL配合volatile是完全正确的单例模式!
解决方案 »
- 问一个JAVA获取当前系统时间并输出的问题
- 看一下我的程序--多线程比较排序算法的快慢
- swing 的问题
- JAVA小数点后面最多可以有几位?用什么数据类型?
- 有道作业题,我真的写不出来了,百分相送,不太难的,希望兄弟们帮帮忙啊:)
- 请教:jdbNavList如何才能出现滚动条?(在线等待)
- 如何把ucs2编码的文本转成GB2312?
- 急!急!急!各位老大,我的开发环境为 jdk1.2 + TexTPad 怎样才能................????
- 如何对HTML文件进行全文检索
- Java Socket客户端与服务器多线程边读边写怎么实现?求大神帮助
- Java 写法 public Spider addUrl(String... urls)
- 学完基础之后的我接下来学什么好
{ if (instance == null)
{ synchronized(Singleton.class) { //1
if (instance == null) //2
instance = new Singleton(); //3
}
}
return instance;}先检查instance是否为null,如果为null则创建新实例。这时锁定Singleton.class。锁定进去了之后,再检查一下instance是否为null,就怕刚才有人已经把instance创建好了。锁定前后各检查一次,就叫双重检查。
{ if (instance == null)
{ synchronized(Singleton.class) { //1
if (instance == null) //2
instance = new Singleton(); //3
}
}
return instance;
个人认为问题不是出现在释放锁 而是博主说的代码优化
就是在创建对象的时候可能会存在1分配内存2创建引用关系3初始化对象 这样的优化创建方式
这样在//3处可能尚未完全创建对象时即2创建引用关系时由于CPU调度当前线程失去CPU
这时在最外层的 if (instance == null)处(非//2处)条件判断失败其他线程会直接返回instance 而此时线程1并未失去锁
另外,据说jdk1.5以后此问题已经被优化了但是。。不太肯定
java的内存模型中,构造函数的执行和instance的内存分配是无顺序的,不是说一定在构造函数执行完,instance才不等于null。
所以存在一种情况,构造函数执行过程中,instance已经不为null,而刚好此时另一个getInstance()调用发生,直接返回了一个初始化不完整的instance
The "Double-Checked Locking is Broken" Declaration
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
如同单例模式中的一样;
{
synchronized(Singleton.class) { //1
if (instance == null) //2
instance = new Singleton(); //3
}
return instance;
而且java在1.2之后就已经有了更好的饿单例实现:利用内部类创建实例,简单高效,没有任何线程问题。
用双重检查锁的单例有线程安全问题 效率好一些。 线程安全问题在于 讲一个没有完全实例化的对象释放出去。public static Singleton getInstance()
{
if (instance == null)//4
{
synchronized(Singleton.class) { //1
if (instance == null) //2
instance = new Singleton(); //3
}
}
return instance;这段代码 中,一个线程看到第四步 发现 不为null,拿去使用了。但是实际是这个不为null的对象,并没有完全实例化。
原因在于: 3处这个地方代码只有一行,真正意义上完成一个对象需要三步。
1,分配对象的内存空间 1买了房子
2,为该对象的属性赋值 2给房子装修
3,初始化该对象。 3 把名字给你。
这个顺序不一定,加入说 3 执行了 再执行2 就出问题了.解决办法是 给instance的修饰加上volatile 。这上这个字后 保证不会重排序。这样得到的就是正确的对象。回答完毕。
第一次检查没有锁定的成本。如果它频繁地在 null 和 not null 之间切换的话,这第一个检查还不如直接省掉算了。