大家都知道单例模式,也知道实现的方法,最近看到几个网友推荐的文章,仔细思考了一下,发现我以前的想法是有问题的。
先看我一直使用的单例代码样例:
系统先判断是否已经初始化(1),
如果没有初始化则进入同步(2),
然后再次判断是否已经初始化(3),
如果还是没有则初始化一个(4).
系统考虑了多CPU问题,使用了 volatile 这个修饰符。 我自认这个代码没有任何问题,但是:??
一个文章里说到的奇怪的执行顺序引起了我的注意,我来说明一下2个线程的执行过程:
线程1这行到了(4),此时系统需要做的事情是给Singleton分配内容,然后进行初始化,可是对instance的赋值操作是在类初始化之前完成的。那没就会出现
线程2 此时拿到了CPU,此时判断instance 已经不再是null,于是他使用了这个已经分配了内存,但没有初始化完毕的类,然后造成运行结果异常。
这个问题我想还是很有可能的,毕竟我们无法保证JVM一定在类已经完全初始化完毕之后再赋值给instance。因为这样做,系统要额外分配一个变量用来临时指向新分配的内存,完成所有的初始化之后再把内存指向instance。而正常的理解,肯定是不会额外的浪费这个变量的(希望我的猜测是错的,我宁可我是错的)。
下面是我再次搜索并整理的更安全的初始化方法,大家参考看看:
第一种,使用静态初始化
第二种,使用
静态
内部类进行初始化
有评价说,第二种更加的安全。我能力有限,实在不能窥透他们的区别,我只能按照保守的估算,在我以后的代码里,全部用第二种方法。
另外,楼下网友提供的用临时变量的方法,你可以参考我的参考文章,里面讲述了理由。在一些编译器优化时,会把它优化掉,造成并不能达到我们要的结果。也就是编译器会认为你的那个临时变量是多余的。
期待牛人能给个更准确的说法。
参考文章:
1 解析 Java 类和对象的初始化过程
2 Java中的模式 --单态
3 The "Double-Checked Locking is Broken" Declaration
先看我一直使用的单例代码样例:
- /**
- * 一段我一直使用的单例实现的代码。
- *
- * @author 赵学庆,Java世纪网(java2000.net)
- *
- */
- public class Singleton {
- private static volatile Singleton instance;
- private Singleton() {
- }
- public static Singleton getInstance() {
- if (instance == null) { // 1
- synchronized (Singleton.class) { // 2
- if (instance == null) // 3
- instance = new Singleton(); // 4
- }
- }
- return instance;
- }
- }
系统先判断是否已经初始化(1),
如果没有初始化则进入同步(2),
然后再次判断是否已经初始化(3),
如果还是没有则初始化一个(4).
系统考虑了多CPU问题,使用了 volatile 这个修饰符。 我自认这个代码没有任何问题,但是:??
一个文章里说到的奇怪的执行顺序引起了我的注意,我来说明一下2个线程的执行过程:
线程1这行到了(4),此时系统需要做的事情是给Singleton分配内容,然后进行初始化,可是对instance的赋值操作是在类初始化之前完成的。那没就会出现
线程2 此时拿到了CPU,此时判断instance 已经不再是null,于是他使用了这个已经分配了内存,但没有初始化完毕的类,然后造成运行结果异常。
这个问题我想还是很有可能的,毕竟我们无法保证JVM一定在类已经完全初始化完毕之后再赋值给instance。因为这样做,系统要额外分配一个变量用来临时指向新分配的内存,完成所有的初始化之后再把内存指向instance。而正常的理解,肯定是不会额外的浪费这个变量的(希望我的猜测是错的,我宁可我是错的)。
下面是我再次搜索并整理的更安全的初始化方法,大家参考看看:
第一种,使用静态初始化
- class Singleton {
- private static volatile Singleton instance = new Singleton();
- private Singleton() {
- }
- public static Singleton getInstance() {
- return instance;
- }
- }
- class Singleton {
- static class SingletonHolder {
- static volatile Singleton instance = new Singleton();
- }
- private Singleton() {
} - public static Singleton getInstance() {
- return SingletonHolder.instance;
- }
- }
另外,楼下网友提供的用临时变量的方法,你可以参考我的参考文章,里面讲述了理由。在一些编译器优化时,会把它优化掉,造成并不能达到我们要的结果。也就是编译器会认为你的那个临时变量是多余的。
期待牛人能给个更准确的说法。
参考文章:
1 解析 Java 类和对象的初始化过程
2 Java中的模式 --单态
3 The "Double-Checked Locking is Broken" Declaration