单例模式:确保一个类只有一个实例,并提供一个全局访问点。单例可以延迟实例化(lazy instantiate)。
// 经典的单件模式实现 public class Singleton { /* 利用一个静态变量来记录Singleton类的唯一实例 */ private static Singleton uniqueInstance; // 这里是其他的有用实例化变量 private Singleton() { /* 把构造器声明为私有的,只有在Singleton类内才可以调用它*/ } /* 用getInstance()方法实例化对象,并返回这个实例 */ public static Singleton getInstance() { if (uniqueInstance == null) { /* 如果我们不需要这个实例,它就永远不会产生,这就是延迟实例化*/ uniqueInstance = new Singleton(); } return uniqueInstance; } // 这里是其他有用的方法 }
有些对象我们只需要一个,例如线程池、缓存、处理偏好设置和注册表的对象、日志对象、充当打印机、显卡等设备的驱动程序的对象。而单例模式给我们了一个全局的访问点,和全局变量一样方便,又没有全局变量的缺点,如果将对象赋值给一个全局变量,那么你必须在程序一开始就创建好对象,如果这个对象非常耗资源,而程序在这次的执行过程中又一直没有用它,这就形成了浪费。
单例模式中的多线程问题:两个线程有可能会创建两个实例,那么该如何处理这种情况呢:
A.只要把getInstance()变成同步(synchronized)方法(会降低性能,其实只有第一次执行此方法时,才需要同步,如果getInstance()的性能对应用程序不是很关键,就用这种方法)
public class Singleton { private static Singleton uniqueInstance; // 其他有用的实例化的变量 Private Singleton() {} Public static synchronized Singleton getInstance() { if (uniqueInstance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; } // 其他有用的方法 }
B. 使用“急切”创建实例(饥渴式),而不用延迟实例化(懒汉式)的做法
public class Singleton { /* 在静态初始化器(static initializen)中创建单件 这保证了线程安全(thread safe)*/ private static final Singleton uniqueInstance = new Singleton(); public static Singleton getInstance() { return uniqueInstance; // 直接返回实例 } }
C.用“双重检查加锁”(double-checked locking,首先检查实例是否已经创建了,如果尚未创建,才进行同步),在个getInstance()中减少使用同步:
public class Singleton { /* volatile关键字确保:当uniqueInstance变量被初始化成Singleton实例时,多个线程正确地处理uniqueInstance变量 */ private volatile static Singleton uniqueInstance; private Singleton() {} public static Singleton getInstance() { if (uniqueInstance == null) { // 检查实例,如果不存在,就进入同步区块 synchronized (Singleton.class) {//只有第一次才彻底执行这里的代码 if (uniqueInstance == null) {//进入区块后,再检查一次 uniqueInstance = new Singleton(); }//Singleton.class是该类对应的字节码文件对象。 } } return uniqueInstance; }
如果使用 多个类加载器,可能导致单例失效而产生多个实例。