23种设计模式之单例模式

单例模式(保证java程序中某个类只有一个实例存在)

单例模式有以下的特点:

  • 单例类只能有一个实例
  • 单例类必须自己创建自己的唯一的实类
  • 单例类必须给其他所有对象提供这一实例

 在计算机系统中,线程池,缓存,日志对象,打印机,对话框常常被设计成单例对象。选择单例模式就是为了避免不一致状态。

一、懒汉式

public class Singleton{

private Singleton singleton = null;

private Singleton(){}
private static Singleton getSingleton(){ if(singleton = null){ singleton = new Singleton(); } return singleton; } }

Singleton通过将构造器私有化,避免了在类外实例化,Singleton的实例只能通过getSingleton()方法访问(不考虑反射)。

以上的懒汉式的代码是没有考虑线程安全的,可以通过以下三种方式将代码进行改造

1.在getSingleton()方法上添加同步(同步是需要开销的,正常执行的代码我们不需要同步,只需要在对象初始化的时候同步)

private static synchronized Singleton getSingleton(){
   if(singleton = null){
      singleton = new Singleton();
   }
}

2.双重检查锁定(double-checked locking)

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

双重检查锁定存在一个问题,这个问题是由指令重排序引起的。指令重排序是为了优化指令,提高程序运行效率,包括编译器重排序和运行时重排序,指令重排序可以在不影响单线程程序执行结果的前提下进行。例如:instance = new Singleton();可以分解为以下伪代码:

memory = allocate();//1.分配对象的内存空间
ctorInstance(memory);//2.初始化对象
instance = memory;//3.instance指向刚才分配的内存空间

经过指令重排序后:

memory = allocate();//1.分配对象的内存空间
instance = memory;//3.instance指向刚才分配的内存空间
ctorInstance(memory);//2.初始化对象

将第2步和第三部调换顺序后,对于单线程的执行结果是没有影响的,但是对于多线程就不一样了。线程A执行了instance = memory(这对另外一个线程B是可见的),线程B这个时候通过 if(instance  == null)判断instance不为空,随机返回,这个时候拿到的是未被完全初始化的实例,在使用的时候会有风险,这就是双重检查锁定存在的问题。

在jdk1.5之后可以使用volatile禁止指令重排序:

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

3.静态内部类(占位类)

public class Singleton {  
    private static class InstanceHolder {  
        public static Singleton instance = new Singleton();  
    }  
  
    private Singleton() {  
    }  
  
    public static Singleton getInstance() {  
        return InstanceHolder.instance;  
    }  
}  

二、饿汉式

public class Singleton{
    
  private  Singleton singleton = new Singleton();
  
  private Singleton(){}

  private Singleton getInstance(){

      return singleton;

   }      
}

猜你喜欢

转载自www.cnblogs.com/liucg1/p/9046424.html