[设计模式]单例模式

  1.   基本的单例模式,分饿汉模式和懒汉模式,饿汉模式在定义变量时就用实例化对象赋值,getInstance()方法中直接返回变量;懒汉模式在定义变量时用null赋值,getInstance()方法中需要判空,如果变量值为null的话需要赋值。可以简单理解为饿汉自己寻找东西吃,懒汉需要别人喂食。
      饿汉模式不存在线程安全的问题,因为在加载类时就已经对其变量赋值。懒汉模式存在线程安全的问题。如果两个线程同时通过了if(instance == null)判断条件,开始执行new操作,这样一来,显然instance被构建了两次。
// 饿汉模式
public class Singleton {
    private static Singleton instance = new Singleton();
    private Singleton(){}

    public static Singleton getInstance() {
        return instance;
    }
}

//懒汉模式
public class Singleton {
    private static Singleton instance = null;
    private Singleton(){}

    public static Singleton getInstance() {
        if (instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

2.  使用synchronized同步锁和双重检测机制(即判断两次)是一般解决线程安全问题的方法。但是此方法在字节码层面还是存在线程安全的问题(指令重排时可能出现问题)。
  指令重排是指一句简单的Java语句,在编译为字节码时可能会经过JVM的优化调整字节码指令顺序。比如java中简单的一句 instance = new Singleton,会被编译器编译成如下JVM指令:如一条new指令会被编译器编译成如下JVM指令:
  memory =allocate(); //1:分配对象的内存空间
  ctorInstance(memory); //2:初始化对象
  instance =memory; //3:设置instance指向刚分配的内存地址
  有可能会被JVM调整为如下顺序:  
  memory =allocate(); //1:分配对象的内存空间
  instance =memory; //2:设置instance指向刚分配的内存地址
  ctorInstance(memory); //3:初始化对象
  那么如果有两个线程A、B,当线程A执行完instance,但是还未进行初始化时,此时instance已经不再指向null。如果此时线程B抢占到CPU资源,会跳过if(instance == null)条件,直接返回一个未被初始化的对象。(解决办法看第三步)

private static Singleton instance = null;
private Singleton(){}

public static Singleton getInstance(){
   if (instance == null){
       synchronized(Singleton.class){
           if(instance == null){
               instance = new Singleton();
           }
       }
   }
   return instance;
}

3.  在instance属性前增加volatile关键字,可以防止指令重排。那么instance对象的引用要么指向null,要么指向一个初始化完毕的对象。具体原因在volatile一文中会说。

private volatile static Singleton instance = null;
private Singleton(){}

public static Singleton getInstance() {
   if(instance == null){
       synchronized (Singleton.class){
           if(instance == null){
               instance = new Singleton();
           }
       }
   }
   return instance;
}

猜你喜欢

转载自blog.csdn.net/vi_nsn/article/details/78909219