HeadFirst 设计模式 5 单例模式(巧克力工厂)

单例模式:

确保一个类只有一个实例,并提供一个全局访问点。

将类设计成自己管理的一个单独的实例,同时也避免其他类再自行产生实例。想要取得单例实例,通过单例类是唯一的途径。

提供实例的全局访问点:当需要实例时,向类查询,他会返回单个实例。

HeadFirst 设计模式 5 单例模式(巧克力工厂)_第1张图片

public class Singleton{
  private static Singleton uniqueInstance;
  //其他有用实例化变量
  private Singleton(){}
  public static Singleton getInstance(){
    if(uniqueInstance==null){
      uniqueInstance=new Singleton();
      }
     return uniqueInstance;
  }
  //其他方法
}

注意:多线程会破坏上述经典的单例实现。

处理多线程

(1)将getInstance()变成同步(synchronized)方法(简单直接但是性能低,对性能无要求可用此方法)

public class Singleton{
  private static Singleton uniqueInstance;
  //其他有用实例化变量
  private Singleton(){}
  
/*通过增加synchronized关键字到getInstance()方法中,
我们迫使每个线程在进入这个方法之前,要先等候别的线程离开该方法。
也就是说,不会有两个线程可以同时进入这个方法*/
  public static synchronized Singleton getInstance(){
    if(uniqueInstance==null){
      uniqueInstance=new Singleton();
      }
     return uniqueInstance;
  }
  //其他方法
}
(2)提前创建实例,而不用延迟实例化的做法(可行)

public class Singleton{
  //在静态初始化器中(static initialize)中创建单件。这段代码保证了线程安全。
  private static Singleton uniqueInstance=new Singleton();
  //其他有用实例化变量
  private Singleton(){}
  public static  Singleton getInstance(){
     return uniqueInstance;  //已有实例,直接使用
  }
  //其他方法
}

(3)用“双重检查加锁”,在getInstance()中减少使用同步(关注性能可用此法)
利用双重检查加锁(double-checked locking),首先检查是否实例已经创建了,如果未创建,“才”进行同步。如此,只有第一次才会同步。

public class Singleton{
  //volatile关键字确保:当uniqueInstance变量被初始化成Singleton实例时,多个线程正确的处理uniqueInstance变量
  private volatile static Singleton uniqueInstance;
  //其他有用实例化变量
  private Singleton(){}
  
  public static  Singleton getInstance(){
  //检查实例,如果不存在,就进入同步区域。只有第一次才彻底执行这些代码。
    if(uniqueInstance==null){  
       synchronized (Singleton.class){
       //再次检查,如果仍是null才创建实例
          if(uniqueInstance==null){
              uniqueInstance=new Singleton();
          }
        }
     return uniqueInstance;
  }
  //其他方法
}

要点:

(1)单例模式确保程序中一个类最多只有一个实例

(2)单例模式提供全局访问点

(3)在Java中实现单例模式需要私有的构造器、一个静态方法和一个静态变量

(4)考虑性能和资源的限制,选择合适的方案实现单例,以解决多线程的问题(必须认定所有的程序都是多线程的)

(5)若使用多个类加载器可能会导致单例失效而产生多个实例

(6)如果使用JVM1.2或之前的版本,必须建立单例注册表,以免垃圾收集器将单例回收

(7)在1.4以及更早的Java版本中许多JVM对volatile关键字的实现会导致双重检查加锁的失效


你可能感兴趣的:(设计模式)