什么是单例模式(Singleton)?

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

这定义已经很明确了,那就是全局只能有这个类的一个实例,举世无双
image.png
这种模式使用的场景实在是太多了,它就好像对某个类实行了“计划生育”政策,限制住了某个类的数量。例如某些类对象需要经常使用,但是如果不停的构造-析构又会消耗不必要的资源;又比如某个资源或文件只能够有一个对象,那就可以通过该模式实现全局的获取。

如何实现

那么如何让全局只有一个类呢?
——需要限制类对象创建,可以将构造函数声明为private,这样就不能在外部通过new来实现了。并且利用static仅有一个全局对象的特点来管理这个类对象。

问题来了,既然构造函数已经被声明为private,怎么获得该类的对象呢?
——简单,在该类的内部声明一个静态成员函数即可。可以通过这个静态成员函数来获取类内部的对象。

单例模式的实现分为懒汉、饿汉模式,主要是从是否在类初始化的时候的创建对象,如果是,那就是饿汉模式,一刻都等不及。

懒汉模式:直到需要该实例的时候才创建对应的实例,最简单的实现。

  1. public class Singleton {
  2. private static Singleton instance;
  3. private Singleton (){}
  4. //需要的时候再创建对应的实例
  5. public static Singleton getInstance() {
  6. if (instance == null) {
  7. instance = new Singleton();
  8. }
  9. return instance;
  10. }
  11. }

考虑到上面的代码线程不安全,可以通过加锁机制改为:

  1. public class Singleton {
  2. private volatile static Singleton singleton;
  3. private Singleton (){}
  4. //采用双检查锁,避免频繁加锁,性能较好
  5. public static Singleton getSingleton() {
  6. if (singleton == null) {
  7. synchronized (Singleton.class) {
  8. if (singleton == null) {
  9. singleton = new Singleton();
  10. }
  11. }
  12. }
  13. return singleton;
  14. }
  15. }

注意,这里使用了两次判断,如果只使用一次判断加锁的话,在单例模式的使用过程中就会有很多次加锁操作。而这些加锁操作是可以避免的,这种方式被称为double-check。

饿汉模式:类初始化的时候就建立该类的一个实例,通过静态变量来实现。

  1. public class SingleObject {
  2. //创建 SingleObject 的一个对象
  3. private static SingleObject instance = new SingleObject();
  4. //让构造函数为 private,这样该类就不会被实例化
  5. private SingleObject(){}
  6. //可以通过该API获取唯一可用的对象
  7. public static SingleObject getInstance(){
  8. return instance;
  9. }
  10. public void showMessage(){
  11. System.out.println("Hello World!");
  12. }
  13. }

总结

总之,单例模式的核心就是保证类对象只有一个。而具体的实现方式则是为了应对不同场景、性能要求而选择的。

参考资料

《大话设计模式》