单例模式是设计模式中使用最为普遍的模式之一。它是一种对象创建模式,用于产生一个对象的具体实例,确保系统中一个类只产生一个实例。在Java中这样的行为能带来两大好处:

  • 对于频繁使用的对象,可以省略new操作花费的时间,这对于那些重量级的对象而言,是非常可观的一笔系统开销;
  • 由于new操作的次数减少,因而对系统内存的使用频率也会降低,这将减轻GC压力,缩短GC停顿时间。

下面将介绍常用的三种创建单例模式的方式:

1:

public class Singleton {

    private Singleton() {}

    private static Singleton instance = new Singleton();

    public static Singleton getInstance() {
        return instance;
    }
}

这种单例模式的性能是非常好的 getInstance()方法只是简单的返回instance 并没有任何锁操作,因此它在并行程序中,会有良好的表现。但是这种方式有一点明显不足,就是Singleton构造函数,或者说Singleton实例在什么时候创建是不受控制的。对于静态成员instance,它会再类第一次初始化的时候被创建,这个时刻并不一定是getInstance()方法第一次被调用的时候。如果觉得这个小小的不足并不重要,我认为这种单例模式是一种不错的选择,它容易实现,易读而且性能优越。

2:

public class LazySingleton {

    private LazySingleton() {
    }

    private static LazySingleton instance = null;

    public static synchronized LazySingleton getInstance() {
        if (instance == null)
            instance = new LazySingleton();
        return instance;
    }
}

这种方式会再第一次调用时,创建单例对象。为了防止对象被多次创建,需要用synchronized进行方法同步。这种实现的好处是,充分利用了延迟加载,只在真正需要是创建对象。但坏处也很明显,并发环境下加锁,竞争激烈的场合对性能产生一定的影响。

3:

public class StaticSingleton {

    private StaticSingleton() {

    }

    private static class SingletonHolder {
        private final static StaticSingleton instance = new StaticSingleton();
    }

    public static StaticSingleton getInstance() {
        return SingletonHolder.instance;
    }
}

这种方式实现的单例同时拥有前两种方式的优点。首先getInstance()方法中没有锁,这使得在高并发环境下性能优越。其次,只有在getInstance()方法被第一次调用时,StaticSingleton的实例才会被创建。因为这种方式巧妙的使用了内部类和类的初始化方式。内部类SingletonHolder被声明为private,这使得我们不可能在外部访问并初始化它。而我们只可能在getInstance()内部对SingletonHolder类进行初始化,利用虚拟机的类初始化机制创建单例。