双重校验锁 -- Java并发编程中的陷阱

来源:互联网 发布:oracle数据库日志位置 编辑:程序博客网 时间:2024/06/04 14:39

今天在回顾volatile关键字的使用的时候,突然注意到其适用场景里有这么一个概念:双重校验锁(DCL)

那么他是什么意思呢?其实它的起源可以联系到单例模式的应用。

import java.util.*;class Singleton{  private static Singleton instance;  //...  private boolean flag;  private Singleton()  {    //...    flag= true;  }//单线程下的获取实例  public static Singleton getInstance()  {    if (instance == null)          //1      instance = new Singleton();  //2    return instance;               //3  }}//多线程下获取实例1(正确)public static synchronized Singleton getInstance(){  if (instance == null)              instance = new Singleton();    return instance;               }//多线程下获取实例2(双重校验)public static Singleton getInstance(){  if (instance == null){             synchronized(instance.class){     if(instance == null)    instance = new Singleton();          }    }  return instance;               }

为什么多线程情况下获取实例的第二个双重校验方法会达不到要求呢?
其实这涉及到JVM编译器的工作,其内存模型允许指令无序写入,也就是可能会对指令进行重排序,从而导致在双重校验的方法中:执行

    instance = new Singleton();  

的时候,按照我们的理解应当是首先产生一个实例对象(初始化完成),随后再进行赋值,然而实际上并不一定是这样进行的,JVM可能在产生了一个空的实力对象之后就进行了赋值,操作,随后再进行(构造函数)初始化操作。如果另一个线程在空实例产生之后,初始化完成之前进行获取实例,其就会因为第一个if判断中就因为条件为true,而返回空的实例,产生错误。

  • 上述中的第一个正确方法是怎么解决这个问题的呢?
    其实很简单,就是给整个方法加了个锁,保证每次只有一个方法得以执行该方法,也就保证最终获得的实例一定是初始化之后的了。
  • 前文我们也讲了,是在看volatile 关键字的时候看到这个问题的,那么volatile 是怎么解决这个问题的呢?
    上面那种方法其实并不高效,在JDK1.5之后,我们可以使用volatile关键字来解决,volatile的特性之一就是禁止指令重排序(内存屏障的概念)。

PS:添加一下单例模式的实现代码

// 1、懒汉式  只适合单线程public class Singleton{    private static Singleton instance;    //...    private Singleton(){        //...    }    public static Singleton getInstance(){        if(instance == null)        instance= new Singleton ();    return instance;}//2、懒汉式 可用于多线程 效率低下 一般情况下不需要同步public class Singleton{    private static Singleton instance;    //...    private Singleton (){        //...    }    private static synchronized Singleton getInstance(){        if(instance == null)        instance= new Singleton ();    return instance;    }}//3、饿汉式 在类装载的时候实例化 但是可能会有其他导致类加载 public class Singleton {    private static Singleton instance = new Singleton ();    //...    private Singleton (){        //...    }     public static Singleton getInstanece(){        return instance;    }}//4、饿汉式  变种public class Singleton{    private static Singleton instance = null;     static{        instance = new Singleton ();    }    //...    private Singleton (){        //...    }    public static Singleton getInstance(){        return instance;    }}//5、静态内部类 也是在classloder机制下保证只有一个实例被创建public class Singleton{    private static class SingletonHolder{        private static final Singleton INSTANCE = new Singleton ();    }    //...    private Singleton(){        //...    }    public static final Singleton getInstance(){        return SingletonHolder.INSTANCE;    }}//6、枚举public class Singleton{    //...    public enum Singleton {      INSTANCE;      public void whateverMethod() {          }      }  }//7、双重校验  上文已述
原创粉丝点击