单例模式备忘

  单例模式需要备忘的部分为:如何创建线程安全的懒汉式单例类

  首先java不能使用DCL写法,如下:

 1     /** 
 2      * 实现单例访问Kerrigan的第四次尝试 
 3      */  
 4     public class SingletonKerriganD {  
 5        
 6         /** 
 7          * 单例对象实例 
 8          */  
 9         private static SingletonKerriganD instance = null;  
10        
11         public static SingletonKerriganD getInstance() {  
12             if (instance == null) {  
13                 synchronized (SingletonKerriganD.class) {  
14                     if (instance == null) {  
15                         instance = new SingletonKerriganD();  
16                     }  
17                 }  
18             }  
19             return instance;  
20         }  
21     }  

  假设线程一执行到instance = new SingletonKerriganD()这句,这里看起来是一句话,但实际上它并不是一个原子操作(原子操作的意思就是这条语句要么就被执行完,要么就没有被执行过,不能出现执行了一半这种情形)。事实上高级语言里面非原子操作有很多,我们只要看看这句话被编译后在JVM执行的对应汇编代码就发现,这句话被编译成8条汇编指令,大致做了3件事情:

  1.给Kerrigan的实例分配内存。

  2.初始化Kerrigan的构造器

  3.将instance对象指向分配的内存空间(注意到这步instance就非null了)。

  但是,由于Java编译器允许处理器乱序执行(out-of-order),以及JDK1.5之前JMM(Java Memory Medel)中Cache、寄存器到主内存回写顺序的规定,上面的第二点和第三点的顺序是无法保证的,也就是说,执行顺序可能是1-2-3也可能是1-3-2,如果是后者,并且在3执行完毕、2未执行之前,被切换到线程二上,这时候instance因为已经在线程一内执行过了第三点,instance已经是非空了,所以线程二直接拿走instance,然后使用,然后顺理成章地报错

  那么实现方法为:

 1 /** 
 2  * 实现单例访问Kerrigan的第六次尝试 
 3  */  
 4 public class SingletonKerriganF {  
 5    
 6     private static class SingletonHolder {  
 7         /** 
 8          * 单例对象实例 
 9          */  
10         static final SingletonKerriganF INSTANCE = new SingletonKerriganF();  
11     }  
12    
13     public static SingletonKerriganF getInstance() {  
14         return SingletonHolder.INSTANCE;  
15     }  

 

posted @ 2013-08-16 15:17  Mr.Ding  阅读(140)  评论(0编辑  收藏  举报