Java DCL简单解析

首先必须声明, 在volatile出现之前,错误的DCL代码如下。在volatile出现之后,正确的DCL代码如下 。代码如下: 
Java代码   收藏代码
  1. //错误的代码  
  2. public class Singleton {  
  3.     private static Singleton instance=null;  
  4.     private Singleton(){}  
  5.     public static Singleton getInstance(){  
  6.         if(instance==null){  
  7.             synchronized (Singleton.class) {  
  8.                 if(instance==null)  
  9.                     instance=new Singleton();//mark行              
  10.             }             
  11.         }  
  12.         return instance;  
  13.     }  
  14. }  

Java代码   收藏代码
  1. //正确的代码  
  2. public class Singleton {  
  3.     private volatile static Singleton instance=null;//添加了volatile修饰符  
  4.     private Singleton(){}  
  5.     public static Singleton getInstance(){  
  6.         if(instance==null){  
  7.             synchronized (Singleton.class) {  
  8.                 if(instance==null)  
  9.                     instance=new Singleton();                 
  10.             }             
  11.         }  
  12.         return instance;  
  13.     }  
  14. }  


我们来剖析错误的代码:  
在错误的DCL代码中,代码行mark在JVM中有2种可能的执行顺序(JVM会对一些没有依赖要求的指令重排序。 关于重排序,在下文中进行说明 ),分别为: 
Java代码   收藏代码
  1. //第一种执行顺序  
  2. mem=allocate()  
  3. mem.initial()  
  4. instance=mem  
  5.   
  6. //第二种执行顺序  
  7. mem=allocate()  
  8. instance=mem  
  9. mem.initial()  

假设现在有2个线程a和b,线程a执行到mark行语句并按照第二种执行顺序执行完instance=mem指令。此时线程b执行getInstane方法会直接返回一个非空的instance,但是这个instance可能是未被完整创建的。这个时候,DCL就出问题了。这就是大家对DCL口诛笔伐的原因。由此可见, 错误的DCL的真正问题就在于:在没有同步的情况下读取一个共享变量,可能读到不完整的实例。 也就是说,不在同步代码块中的if(instance==null)代码可能读取到不完整的instance实例。 

为了解决类似DCL中出现的问题, JAVA在JMM中(JAVA内存模型)定义了一系列Happens-Before关系。如果两个操作之间缺少Happens-Before关系,那么JVM就可以对它们任意的重排序。 比如volatile变量规则规定:对volatile变量的写入操作必须在对该变量的读操作之前进行。这就是DCL在使用volatile之后变得正确的原因。 

重排序:  
下面的代码可能的输出结果有:<1,1><1,0><0,1><0,0>这四种,首先,两个线程的先后执行顺序不同,可能one先(结果是<0,1>),可能two先(结果是<1,0>),可能one和two交替执行(结果是<1,1>);其次,由于每个线程内部的各个操作之间不存在依赖性,因此这些操作可以重排序(结果可能为<0,0>)。可见, 内存级别的重排序会令程序变得不可预测,因此需要正确的使用同步,以使程序满足JMM要求的可见性规则。  
Java代码   收藏代码
  1. public class Demo{  
  2.     static int x=0,y=0;  
  3.     static int a=0,b=0;  
  4.     public static void main(String [] args) throws InterruptedException {  
  5.         Thread one=new Thread(new Runnable() {  
  6.             @Override  
  7.             public void run() {  
  8.                 a=1;  
  9.                 x=b;                  
  10.             }  
  11.         });  
  12.         Thread two=new Thread(new Runnable() {  
  13.             @Override  
  14.             public void run() {  
  15.                 b=1;  
  16.                 y=a;  
  17.             }  
  18.         });  
  19.         one.start();    two.start();  
  20.         one.join();     two.join();  
  21.         System.out.println("<"+x+","+y+">");  
  22.     }  

  1. }  
  2. 转自:http://yizhenn.iteye.com/blog/2294121

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值