[设计模式]单件模式概念和三个解决多线程问题的方案

主要是从 Head Fisrt 设计模式中学习到知识;

1. 定义单件模式

  单件模式确保一个类只有一个实例,并提供一个全局访问点;

  在整个系统上下文中,只有一个对象,对于很多在系统中只需要一个或者创建代价比较大的对象,可以使用,例如:线程池、缓存、对话框、处理偏好设置和注册表对象、日志对象、充当打印机、显卡等设备的驱动程序的对象;

  采用单件模式可以避免系统维护太多没有记录状态数据、所有实例功能可替代的相关对象,还有就是多个实例处理会可能会造成结果不一致的问题;

2. 主要思想

  2.1 由类持有一个静态的类对象,并通过方法暴露对类对象的获取接口;

  2.2 类禁止外部对象实例化

3. 单件模式的实现

  3.1 下面是单件模式的简单实现,思想就是持有一个静态对象,当需要的时候就通过类的公用方法获取对象的引用,这里还有一个做法就是延迟实例化,当对象需要用到的时候才会去实例化对象,如果对象没有被引用到,那么就是一直都不会实例化,系统资源也不会浪费。

public class Singleton {
  private static Singleton uniqueInstance;

  private Singleton () {}

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

  public static void main(String[] args) {

    Singleton singleton = Singleton.getInstance();  
    System.out.println(singleton.toString());    // Singleton@15db9742

    singleton = Singleton.getInstance();
    System.out.println(singleton.toString());    // Singleton@15db9742

    singleton = Singleton.getInstance();
    System.out.println(singleton.toString());    // Singleton@15db9742

    singleton = Singleton.getInstance();
    System.out.println(singleton.toString());    // Singleton@15db9742

  }
}

  这里打印出来,几个类的对象的内存地址都是同一个,引用了同一个对象。

4. 单件模式和多线程

  上面的单件模式实现已经能够满足基本的使用要求,但是当单件模式模式遇到多线程之后,很多奇怪的问题就发生了(事实上很多代码遇到多线程后都会有问题),

  例如上面的例子,当代码的推进状态像下面的状态,就会出现问题:

  这个时候对象已经不是同一个了,多线程造成 单件模式 已经和我们定义的不一样了。

  为了解决这个问题,有以下的三个解决方法:

  * 使用synchronized方法,将getInstance()变成同步的方法,这样能够解决我们的问题,但是实例化只是在第一次的时候使用,后面就没有这个问题存在了,然后通过synchronized强制同步会降低系统的并发性能,这种情况适合getInstance()的性能不影响或者影响了可以接受的地方;

  

 public static synchronized Singleton getInstance () {
...

  * 使用“急切”的实例化方法,在类初始化的时候就像对象生成;

public class Singleton {
  private static Singleton uniqueInstance = new Singleton();
  private Singleton () {}
  public static Singleton getInstance () { 
    return uniqueInstance;  
  }
}

  * 使用“双重检查加锁”( since jdk 1.4 )

  使用之后代码是这样子的:

  

public class Singleton {
  private volatile static Singleton uniqueInstance;

  private Singleton () {}

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

  public static void main(String[] args) {
    System.out.println("test");
    Singleton singleton = Singleton.getInstance();
    System.out.println(singleton.toString());

    singleton = Singleton.getInstance();
    System.out.println(singleton.toString());
    singleton = Singleton.getInstance();
    System.out.println(singleton.toString());
    singleton = Singleton.getInstance();
    System.out.println(singleton.toString());
  }
}

  通过 volatile 标识该变量的修改是对其他线程可见的,还有禁止指令排序;详细用法见:地址

  然后当判断是未初始化的时候,再将类进入锁定然后再次判空之后再初始化类,这样就解决了我们前面遇到的问题,也很好的实现了单件模式的理念。

猜你喜欢

转载自www.cnblogs.com/zhuangmingnan/p/9383859.html