精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2013-12-10
private static Singleton singleton; //错误, 这里应该加上volatile关键字, 保证singleton可见性, 从而能够安全发布 private Singleton(){ } public static Singleton getInstance(){ // 双重检查加锁 if(singleton==null){ synchronized(Singleton.class){ // 延迟实例化,需要时才创建 if(singleton==null) singleton = new Singleton();//错误原因,singleton = new Singleton();不是一个原子操作,有些虚拟机可能出现在类没有完成初始化,singleton 就已经不为null。线程并发时有小概率会产生问题。所以应该加上volatile, } } return singleton; } } ------------------------------------- 那么不使用volatile关键字能否实现安全DCL呢?答案当然是肯定的,有些文章说,在jdk1.5之前(1.5之前没有volatile关键字),无法实现DCL的singleton,这种说法是错误的。废话少说,上代码 ------------------------------------- public class Singleton { private static Singleton singleton; // 这类没有volatile关键字 private Singleton() { } public static Singleton getInstance() { // 双重检查加锁 if (singleton == null) { synchronized (Singleton.class) { // 延迟实例化,需要时才创建 if (singleton == null) { Singleton temp = null; try { temp = new Singleton(); } catch (Exception e) { } if (temp != null) singleton = temp; //为什么要做这个看似无用的操作,因为这一步是为了让虚拟机执行到这一步的时会才对singleton赋值,虚拟机执行到这里的时候,必然已经完成类实例的初始化。所以这种写法的DCL是安全的。由于try的存在,虚拟机无法优化temp!=null 是否为true } } } return singleton; } } 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2013-12-14
所谓的懒汉式加载就是一些技术偏执狂搞出来唬人的写法,我觉得根本没必要。。代码既然写了就用得到,早加载晚加载都要加载,加载还只加载一次。。以前看过一篇文章,双重检查加锁并不能确切的实现单例,从此再也不写这种高深的代码啦。。
|
|
返回顶楼 | |
发表时间:2013-12-17
看来楼主还是不理解为什么要加volatile,简单说,你所说的那种方式,理论上也不是线程安全的,注意new的几个过程
|
|
返回顶楼 | |
发表时间:2013-12-17
要么类载入时就创建实例,要么就性能差的synchronized方法,懒汉加载不定就出什么问题……
|
|
返回顶楼 | |
发表时间:2013-12-17
流风suiyue 写道 所谓的懒汉式加载就是一些技术偏执狂搞出来唬人的写法,我觉得根本没必要。。代码既然写了就用得到,早加载晚加载都要加载,加载还只加载一次。。以前看过一篇文章,双重检查加锁并不能确切的实现单例,从此再也不写这种高深的代码啦。。
这种代码是有实际意义的。 比如A模块的启动需要依赖B模块的启动。B模块可能是另外一个系统,那么你就必须使用懒加载方式。 |
|
返回顶楼 | |
发表时间:2013-12-17
最后修改:2013-12-17
iamiwell 写道 看来楼主还是不理解为什么要加volatile,简单说,你所说的那种方式,理论上也不是线程安全的,注意new的几个过程
看来是你没有真正理解new的过程。 A a = new A(); ...//注意后面代码 new的过程只会影响a的赋值先后顺序,这是因为new的过程不是一个原子造成的。但是不管new的过程有多少,new语句后面的代码都不会在new的过程结束之前执被行。 另外还必须考虑虚拟机优化问题,这里就不仔细说了。 |
|
返回顶楼 | |
发表时间:2013-12-17
qiaoenxin 写道 iamiwell 写道 看来楼主还是不理解为什么要加volatile,简单说,你所说的那种方式,理论上也不是线程安全的,注意new的几个过程
看来是你没有真正理解new的过程。 A a = new A(); ...//注意后面代码 new的过程只会影响a的赋值先后顺序,这是因为new的过程不是一个原子造成的。但是不管new的过程有多少,new语句后面的代码都不会在new的过程结束之前执被行。 另外还必须考虑虚拟机优化问题,这里就不仔细说了。 ... 20: new #3; //class Singleton 23: dup 24: invokespecial #4; //Method "<init>":()V <assigns> ... new执行之后,this地址即已经可以使用了,但是构造工作并没有全部完成,在这种情况下, 不管怎么去判断和赋值,都不能保证最终拿到的instance是已经构造好了 至于你的try,这个是防止java编译的优化,而“但是不管new的过程有多少,new语句后面的代码都不会在new的过程结束之前执被行。 ”这句话,是没有根据的,其实你描述的就是一个屏障,没有规范说每个构造器都必须添加屏障,这也是为什么可以使用volatile或final,因为它们就是一个屏障,正如你所说,你也认同构造的过程并不是原子的么 总的来说,理论上这种方式不是线程安全的,但实际中,有可能某些处理器的特殊性,能够保证它的线程安全,或者发生线程安全问题的场景基本不可见 也可以看看类似的文章 http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html or http://ifeve.com/double-checked-locking-with-delay-initialization/ |
|
返回顶楼 | |
发表时间:2013-12-17
iamiwell 写道 qiaoenxin 写道 iamiwell 写道 看来楼主还是不理解为什么要加volatile,简单说,你所说的那种方式,理论上也不是线程安全的,注意new的几个过程
看来是你没有真正理解new的过程。 A a = new A(); ...//注意后面代码 new的过程只会影响a的赋值先后顺序,这是因为new的过程不是一个原子造成的。但是不管new的过程有多少,new语句后面的代码都不会在new的过程结束之前执被行。 另外还必须考虑虚拟机优化问题,这里就不仔细说了。 ... 20: new #3; //class Singleton 23: dup 24: invokespecial #4; //Method "<init>":()V <assigns> ... new执行之后,this地址即已经可以使用了,但是构造工作并没有全部完成,在这种情况下, 不管怎么去判断和赋值,都不能保证最终拿到的instance是已经构造好了 至于你的try,这个是防止java编译的优化,而“但是不管new的过程有多少,new语句后面的代码都不会在new的过程结束之前执被行。 ”这句话,是没有根据的,其实你描述的就是一个屏障,没有规范说每个构造器都必须添加屏障,这也是为什么可以使用volatile或final,因为它们就是一个屏障,正如你所说,你也认同构造的过程并不是原子的么 总的来说,理论上这种方式不是线程安全的,但实际中,有可能某些处理器的特殊性,能够保证它的线程安全,或者发生线程安全问题的场景基本不可见 也可以看看类似的文章 http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html or http://ifeve.com/double-checked-locking-with-delay-initialization/ 我觉得楼主try那个版本没什么问题。 你注意看下,他是先给try 给temp 赋值 new Singleton(), 而不是给 instance 赋值。 所以不会发生没 new 到一半 instance已经可用的情况。 |
|
返回顶楼 | |
发表时间:2013-12-18
iamiwell 写道 qiaoenxin 写道 iamiwell 写道 看来楼主还是不理解为什么要加volatile,简单说,你所说的那种方式,理论上也不是线程安全的,注意new的几个过程
看来是你没有真正理解new的过程。 A a = new A(); ...//注意后面代码 new的过程只会影响a的赋值先后顺序,这是因为new的过程不是一个原子造成的。但是不管new的过程有多少,new语句后面的代码都不会在new的过程结束之前执被行。 另外还必须考虑虚拟机优化问题,这里就不仔细说了。 ... 20: new #3; //class Singleton 23: dup 24: invokespecial #4; //Method "<init>":()V <assigns> ... new执行之后,this地址即已经可以使用了,但是构造工作并没有全部完成,在这种情况下, 不管怎么去判断和赋值,都不能保证最终拿到的instance是已经构造好了 至于你的try,这个是防止java编译的优化,而“但是不管new的过程有多少,new语句后面的代码都不会在new的过程结束之前执被行。 ”这句话,是没有根据的,其实你描述的就是一个屏障,没有规范说每个构造器都必须添加屏障,这也是为什么可以使用volatile或final,因为它们就是一个屏障,正如你所说,你也认同构造的过程并不是原子的么 总的来说,理论上这种方式不是线程安全的,但实际中,有可能某些处理器的特殊性,能够保证它的线程安全,或者发生线程安全问题的场景基本不可见 也可以看看类似的文章 http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html or http://ifeve.com/double-checked-locking-with-delay-initialization/ 你的问题7楼已经给你解释了。你需要注意temp变量的作用。 至于你给出的文章,我刚学java的时候就已经看过内容相似的文章。起初我也和你的想法一样,后来相关文章也拜读了不少,才发现那篇文章的问题。 对于A a = new A(); 这个过程大致做了3件事情 1.给实例分配内存。 2.初始实例的构造器 3.将变量指向实例对象的内存空间。 我姑且称为new的三个过程为一个执行单元unit(1,2,3)。虚拟机总是尽可能多的将一个执行单元中可以并发执行的元素(1,2,3)同时提交给cpu乱序执行,提高执行效率。 |
|
返回顶楼 | |
发表时间:2013-12-18
qiaoenxin 写道 iamiwell 写道 qiaoenxin 写道 iamiwell 写道 看来楼主还是不理解为什么要加volatile,简单说,你所说的那种方式,理论上也不是线程安全的,注意new的几个过程
看来是你没有真正理解new的过程。 A a = new A(); ...//注意后面代码 new的过程只会影响a的赋值先后顺序,这是因为new的过程不是一个原子造成的。但是不管new的过程有多少,new语句后面的代码都不会在new的过程结束之前执被行。 另外还必须考虑虚拟机优化问题,这里就不仔细说了。 ... 20: new #3; //class Singleton 23: dup 24: invokespecial #4; //Method "<init>":()V <assigns> ... new执行之后,this地址即已经可以使用了,但是构造工作并没有全部完成,在这种情况下, 不管怎么去判断和赋值,都不能保证最终拿到的instance是已经构造好了 至于你的try,这个是防止java编译的优化,而“但是不管new的过程有多少,new语句后面的代码都不会在new的过程结束之前执被行。 ”这句话,是没有根据的,其实你描述的就是一个屏障,没有规范说每个构造器都必须添加屏障,这也是为什么可以使用volatile或final,因为它们就是一个屏障,正如你所说,你也认同构造的过程并不是原子的么 总的来说,理论上这种方式不是线程安全的,但实际中,有可能某些处理器的特殊性,能够保证它的线程安全,或者发生线程安全问题的场景基本不可见 也可以看看类似的文章 http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html or http://ifeve.com/double-checked-locking-with-delay-initialization/ 你的问题7楼已经给你解释了。你需要注意temp变量的作用。 至于你给出的文章,我刚学java的时候就已经看过内容相似的文章。起初我也和你的想法一样,后来相关文章也拜读了不少,才发现那篇文章的问题。 对于A a = new A(); 这个过程大致做了3件事情 1.给实例分配内存。 2.初始实例的构造器 3.将变量指向实例对象的内存空间。 我姑且称为new的三个过程为一个执行单元unit(1,2,3)。虚拟机总是尽可能多的将一个执行单元中可以并发执行的元素(1,2,3)同时提交给cpu乱序执行,提高执行效率。 哎,没什么话好说了,请注意,已经拿到this的地址了,当然可以直接给temp,这个时候temp也不是null,也可以赋值给singleton,但这整个过程中,都不能保证构造函数执行完成,这种问题其实讨论的也没什么意思,我只是好心指出来而已 |
|
返回顶楼 | |