特点
- 一个类只有一个实例对象
- 一般用在管理共享的资源上,例如线性池、缓存、对话框、偏好设置、注册表、日志的对象,及充当打印机、显卡等设备的驱动程序的对象,等等
基本结构
- 私有静态变量记录类的唯一实例
- 私有构造器,只有类内才可以调用
- 公共或保护型竞赛方法getInstance()实例化对象,提供全局访问点,并返回该对象
基本代码
Public class Singleton{
private static Singleton uniqueInstance; //私有静态变量记录类的唯一实例
private Singleton(){}; //私有构造器,只有类内才可以调用
public static Singleton getInstance(){
If(uniqueInstance==null){ //如果uniqueInstance为空,表示还没有实例化,实例化之后再返回
uniqueInstance=new Singleton(); //不存在就实例化,不需要永不实例化,即“延迟实例化”
}
return uniqueInstance; //表示已存在对象,返回该对象
}
}
存在问题
存在多线程的问题,即多个线程同时进入if判断,然后先后创建多个对象,利用synchronized(同步)解决
例如:
public static synchronized Singleton getInstance(){}
这样虽然避免了多个线程同时调用方法的问题,但是会产生另外一个比较严重的问题:就是每次调用这个方法的时候都需要同步,然而只有第一次执行这个方法才真正需要同步,故而每次都同步其实是一种累赘。
那么该如何解决同步累赘问题呢?有三种方法:
第一种,如果getInstance()的性能对应用程序不是很关键,就什么都别做。即不会产生同步影响的就不需要加synchronized了,因为同步一个方法可能会造成程序执行效率下降,故而当频繁调用getInstance()的时候,就需要考虑了。
第二种,使用“急切”创建实例,而不用延迟到实例化的做法。如果应用程序中是创建并使用单例实例,或者在创建或运行时方面的负担不太繁重,你可能想要机器创建此单例,如:
Public classSingleton{
privatestatic Singleton uniqueInstance=new Singleton();//静态初始化器创建实例,保证线程安全
privateSingleton(){};
publicstatic Singleton getInstance() {
return uniqueInstance; //已经有实例,直接使用它
}
}
即,在我需要使用实例之前就已经创建好了实例,使用时直接返回创建好的实例就OK。
第三种,用“双重检查加锁”,在getInstance()中减少使用同步。首先检查是否实例已经创建了,如果未创建,菜进行同步,这样一来,只有第一次会同步,正是我们想要的。
Public classSingleton{
private volatile static Singleton uniqueInstance; //volatile确保多个线程正确地处理uniqueInstance变量
privateSingleton(){};
publicstatic Singleton getInstance(){
If(uniqueInstance==null){ //检查实例,如果不存在,就进入同步区块
Synchronized(Singleton.class){ //只有第一次才彻底执行这里
If(uniqueInstance==null){
uniqueInstance=new Singleton(); //进入区块后,再检查一次,如果仍是null,才创建实例
}
}
}
returnuniqueInstance;
}
}
如果性能是关心的重点,则双重锁的做法可以大大减少getInstance()的时间耗费。