设计模式之单例模式

Singleton Pattern 简介

定义

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

用途

  • 有一些对象我们只需要一个,比如:线程池(threadpool)、缓存(cache)、注册表设置对象(registry setting),确保程序中使用的全局资源只有一份。
  • 管理共享资源

类图

处理多线程

如果 getInstance() 的性能对应用程序不是很关键,可以用下面的方式:

public class Singleton {
    private static Singleton uniqueInstance;

    private Singleton(){}

    /**
     * 通过增加 synchronized 关键字到 getInstance 中,我们迫使每个线程在进入这个方法之前,
     * 要先等候别的线程离开该方法。
     * @return
     */
    public static synchronized Singleton getInstance(){
        if (uniqueInstance == null){
            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }
}

这样可以解决问题,但是同步降低了性能。还有一点:只有第一次执行此方法时,才真正需要同步,换句话说,一旦设置好 uniqueInstance 变量,就不再需要同步这个方法了,之后每次调用这个方法,同步都是一种负担。

使用急切创建实例,而不用延迟实例化的做法(饿汉式)

如果应用程序总是创建并使用单例实例,或者在创建和运行时方面的负担不太繁重,可以用这种方式:

public class Singleton {

    //在静态初始化器中创建单例,这段代码保证了线程安全
    private static Singleton uniqueInstance = new Singleton();

    private Singleton(){}

    public static synchronized Singleton getInstance(){
        return uniqueInstance;
    }
}

这样,JVM在加载这个类时马上创建此唯一的单例,保证了在任何线程访问 uniqueInstance 静态变量之前,一定先创建此实例。

双重检查加锁,在 getInstance() 中减少使用同步

如果性能是重点,这个方法会大大地减少 getInstance() 的时间耗费。

public class Singleton {

    //volatile 关键字确保,当 uniqueInstance 变量被初始化成 Singleton 实例时,
    //多个线程正确地处理 uniqueInstance 变量
    private volatile static Singleton uniqueInstance;

    private Singleton(){}

    /**
     * 检查实例,如果不存在,则进入同步块
     * @return
     */
    public static synchronized Singleton getInstance(){
        if (uniqueInstance == null){
            //只有第一次才彻底执行这里的代码
            synchronized (Singleton.class){
                //进入这里后,在检查一次,如果仍然是 null 才创建
                if (uniqueInstance == null){
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}