1.定义

单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态。

2.实现方式

2.1 单线程版

public class SingleThreadedSingleton {
  // 保存该类的唯一实例
  private static SingleThreadedSingleton instance = null;

  // 省略实例变量声明
  /* * 私有构造器使其他类无法直接通过new创建该类的实例 */
  private SingleThreadedSingleton() {
    // 什么也不做
  }

  /** * 创建并返回该类的唯一实例 * 即只有该方法被调用时该类的唯一实例才会被创建 * * @return */
  public static SingleThreadedSingleton getInstance() {
    if (null == instance) {
      instance = new SingleThreadedSingleton();
    }
    return instance;
  }

  public void someService() {
    // 省略其他代码
  }
}

通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,SingleThreadedSingleton的唯一实例只能通过getInstance()方法访问。在多线程环境下,getlnstance()中的 if 语句形成一个 check-then-act 操作,它不是一个原子操作。所以不是线程安全的。

2.2 简单加锁实现

public class SimpleMultithreadedSingleton {
  // 保存该类的唯一实例
  private static SimpleMultithreadedSingleton instance = null;

  /* * 私有构造器使其他类无法直接通过new创建该类的实例 */
  private SimpleMultithreadedSingleton() {
    // 什么也不做
  }

  /** * 创建并返回该类的唯一实例 <BR> * 即只有该方法被调用时该类的唯一实例才会被创建 * * @return */
  public static SimpleMultithreadedSingleton getInstance() {
    synchronized (SimpleMultithreadedSingleton.class) {
      if (null == instance) {
        instance = new SimpleMultithreadedSingleton();
      }
    }
    return instance;
  }

  public void someService() {
    // 省略其他代码
  }
}

在方法上加synchronized同步锁或是用同步代码块对类加同步锁,此种方式虽然解决了多个实例对象问题,但是该方式运行效率却很低下,下一个线程想要获取对象,就必须等待上一个线程释放锁之后,才可以继续运行。
而且只有第一次执行此方法时,才真正需要同步。换句话说,一旦创建好对象,就不再需要同步这个方法了,之后每次调用这个方法,同步都是一种累赘。

2.3 双重检查锁定

public class DCLSingleton {
  /* * 保存该类的唯一实例,使用volatile关键字修饰instance。 */
  private static volatile DCLSingleton instance;

  /* * 私有构造器使其他类无法直接通过new创建该类的实例 */
  private DCLSingleton() {
    // 什么也不做
  }

  /** * 创建并返回该类的唯一实例 <BR> * 即只有该方法被调用时该类的唯一实例才会被创建 * * @return */
  public static DCLSingleton getInstance() {
    if (null == instance) {// 操作①:第1次检查
      synchronized (DCLSingleton.class) {
        if (null == instance) {// 操作②:第2次检查
          instance = new DCLSingleton();// 操作③
        }
      }
    }
    return instance;
  }

  public void someService() {
    // 省略其他代码
  }
}

在执行临界区代码前先检查 instance 是否为 null; 若 instance 不为 null, 则 getlnstance(),直接返回,否则才执行临界区。减少了申请锁的次数。注意一点:instance必须用volatile关键字修饰,否则,一个线程在执行操作①的时候,发现 instance 不为 null, 于是该线程就直接返回这个 instance 变量所引用的实例,而由于JIT编译器的重排序,这个实例可能是另一个线程创建但未初始化完毕的。

2.4 立即创建实例方式

public class Singleton {
	private static Singleton uniqueInstance = new Singleton();
 
	private Singleton() {}
 
	public static Singleton getInstance() {
		return uniqueInstance;
	}
	
	// other useful methods here
	public String getDescription() {
		return "I'm a statically initialized Singleton!";
	}
}

在类加载初始化时就创建好一个静态的对象供外部使用,除非系统重启,这个对象不会改变,所以本身就是线程安全的。这种方式不能实现“延迟创建”,如果应用程序总是创建并使用单件实例,或者在创建和运行时方面的负担不太繁重,可以使用这种方式。

2.5 静态内部类实现

public class StaticHolderSingleton {
  // 私有构造器
  private StaticHolderSingleton() {
    Debug.info("StaticHolderSingleton inited.");
  }

  static class InstanceHolder {
    // 保存外部类的唯一实例
    static {
      Debug.info("InstanceHolder inited.");
    }
    final static StaticHolderSingleton INSTANCE = new StaticHolderSingleton();
  }

  public static StaticHolderSingleton getInstance() {
    Debug.info("getInstance invoked.");
    return InstanceHolder.INSTANCE;
  }

  public void someService() {
    Debug.info("someService invoked.");
    // 省略其他代码
  }

  public static void main(String[] args) {
	StaticHolderSingleton.getInstance().someService();
  }
}

类的静态变量被初次访问会触发 Java 虚拟机对该类进行初始化,即该类的静态变量的值会变为其初始值而不是默认值 。 因此,静态方法 getlnstance()被调用的时候 Java 虚拟机会初始化这个方法所访问的内部静态类 InstanceHolder 。 这使得 InstanceHolder 的静态变量INSTANCE 被初始化,从而使 StaticHolderSingleton 类的唯一实例得以创建。
由于类的静态变蜇 只会创建一次,因此 StaticHolderSingleton (单例类)只会被创建一次。

2.6 利用枚举类实现

public class EnumBasedSingletonExample {
  public static void main(String[] args) {
    Thread t = new Thread() {
      @Override
      public void run() {
        Debug.info(Singleton.class.getName());
        Singleton.INSTANCE.someService();
      };
    };
    t.start();
  }

  public static enum Singleton {
    INSTANCE;
    // 私有构造器
    Singleton() {
      Debug.info("Singleton inited.");
    }

    public void someService() {
      Debug.info("someService invoked.");
      // 省略其他代码
    }
  }
}

这里,枚举类型 Singleton 相当于一个单例类,其字段 INSTANCE 值相当于该类的唯一实例。这个实例是在 Singleton.INSTANCE 初次被引用的时候才被初始化的。 仅访问 Singleton 本身(比如 Singleton.class.getName()调用)并不会导致 Singleton 的唯一实例被初始化 。