一、概述
一般问题:有时候我们需要一个单一的全局对象,来协调系统整体的行为。
核心方案:只允许此类有一个实例存在。
设计意图:要让一个类只能有一个实例存在,首先要控制其构造方法,将其设为私有,使其只能在内部实例化。其次,提供一个静态方法,使外部可以通过此方法获取唯一实例。
二、单例的写法
单例模式的写法分为“饿汉式”和“懒汉式”,区别就是实例化的时机,一种是类加载时实例化;另一种是首次调用时实例化。
“饿汉式”
public class Singleton { //类加载时实例化 private final static Singleton INSTANCE = new Singleton(); //构造方法私有化 private Singleton(){} //静态方法获取唯一实例 public static Singleton getInstance(){ return INSTANCE; } }
这种写法比较简单,在类装载的时候就完成实例化,避免了线程同步问题。缺点是提前占用了内存,造成内存浪费。
“懒汉式”
public class Singleton { private static Singleton singleton; private Singleton() {} public static Singleton getInstance() { //首次调用时实例化 if (singleton == null) { singleton = new Singleton(); } return singleton; } }
这种“懒汉式”写法起到了Lazy Loading的效果,但是只能在单线程下使用。如果在多线程下可能产生多个实例。
“懒汉式”升级版
public class Singleton { private static volatile Singleton singleton; private Singleton() {} public static Singleton getInstance() { if (singleton == null) { //此处对代码块做线程同步,比直接对方法做线程同步更高效,因为只有首次实例化时起作用 synchronized (Singleton.class) { //这里双重判空(double-check)是必要的,否则依然可以产生多个实例 if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }
这种写法采用double-check的概念既保证了线程安全,又保证了效率。
三、总结
优点:减少内存开销,避免资源多重占用
缺点:不能继承,扩展困难
总结:单例模式是一种创建型设计模式,在对系统资源统一管理,化繁为简,避免“政出多头”方面有很好的优势。但我们不能仅仅为了节省资源而滥用单例模式。其保持单一实例必然还是为了达到用户特定的使用目的。比如有多个线程要同时进行文件创建、打开、修改一个文件的操作时,就用到单例模式设计文件管理器。
用一句话表述单例模式:
饿汉,懒汉,双重检验