黑马程序员-java基础

---------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity开发</a>、<a href="http://www.itheima.com"target="blank">.Net培训</a>、期待与您交流! ----------------------

先来看看延时加载的单例代码: 

Java代码   收藏代码
  1. public static Singleton getInstance()  
  2.   {  
  3.     if (instance == null)          //1  
  4.       instance = new Singleton();  //2  
  5.     return instance;               //3  
  6.   }  

由于以上没有采用同步,因此多线程环境下可能会创建多个实例。 
那么我们加上synchronized,在方法中加了同步块: 
Java代码   收藏代码
  1. public static Singleton getInstance()  
  2. {  
  3.   if (instance == null)  //1  
  4.   {  
  5.     synchronized(Singleton.class) {  //2  
  6.       instance = new Singleton();  
  7.     }  
  8.   }  
  9.   return instance; //3  
  10. }  

同步块包围了单例对象的创建, 看似这样就是个典型的延时加载的单例模式了。但还有一个问题,假设在线程A运行到//1的时候, 被线程B抢占了cpu,并运行到了//3,那么线程A继续执行就又会重复创建对象了,从而就破坏了单例的规则。 
ok,那么我们再做一个修改,代码变成这样: 
Java代码   收藏代码
  1. public static Singleton getInstance()  
  2. {  
  3.   if (instance == null)    //1  
  4.   {  
  5.     synchronized(Singleton.class) {    
  6.       if (instance == null)          //2  
  7.         instance = new Singleton();  //3  
  8.     }  
  9.   }  
  10.   return instance;  
  11. }  

我们看到, 除了//1进行了一次非null检查,在同步块代码//3处又加入了一次检查,这就是double-checked locking (DCL),这样就避免发生像上面的在其他线程又重复创建单例对象的问题,看起来好像是完美了。 
不幸的是, 即使是DCL也可能会失败。 
instance = new Singleton(); 
这么简单一行创建对象的代码,就分为几步: 
1)Allocate memory, 
2)invoke constructor, 
3)give reference 
如果这几步顺序就是1),2),3) 那没问题,不过JIT编译器不保证2)和3)的顺序, 于是可能变成了1),3),2)  看看这时可能会发生什么: 
线程A先进入同步块,执行 instance = new Singleton(), 这时instance 只拿到了ref,但构造器还未执行,退出了同步块; 
线程B切入, 这时instance 是 非null的(上面线程A已经分配了ref),于是就return了 instance,而这个instance 却是没有经过构造初始化的,再访问instance就会导致错误。 
这就是JITcompilers的out-of-order问题, 导致了初始化和变量赋值的顺序不可预料,产生以上问题。

---------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity开发</a>、<a href="http://www.itheima.com"target="blank">.Net培训</a>、期待与您交流! ----------------------

详细请查看:<a href="http://www.itheima.com" target="blank">www.itheima.com</a>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值