单例模式 ------确保对象的唯一性
3个要点(以TaskManager为例):
- <1>私有化构造器:
private TaskManager(){...}
- <2> 定义静态私有成员变量
private static TaskManager tm = null;
- <3> 增加公有静态方法
public static TaskManager getInstance(){ if(tm == null){ tm = new TaskManager(); } return tm; }
饿汉式VS懒汉式
饿汉式
- 结构图
- 代码实现
Class EagerSingleton {
private static final EagerSingleton instance = new EagerSinleton();
private EagerSingleton ()
public static EagerSingleton getinstance(){
return instance;
}
}
懒汉式单例类与线程锁定
<1>方法前加进行synchronized线程锁定
public class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton(){}
synchronized public static LazySingleton getInstance(){
if (instance == null){
instance = new LazySingleton();
}
return instance;
}
}
<2>上述代码虽然解决了线程安全问题,但每次调用getinstance()时都需要进行线程锁定判断,在多线程高并发访问环境中,将会导致系统性能大大降低所以继续改进
public static LazySingleton getInstance(){
if(instance == null){
synchronized(LazySingleton.class) {
instance = new LazySingleton();
}
}
return instance;
}
<3>双重检查锁定(最终版)
如果某一瞬间线程A和线程B都在调用getinstance()方法,此时instance对象认为null值,均能通过“instance == null”的判断。当A执行完毕后,B仍然会执行。所以需要双重锁定。
public class LazySingleton {
private volatile static LazySingleton instance = null;
private LazySingleton(){}
public static LazySingleton getInstance(){
if(instance == null){
synchronized(LazySingleton.class) {
if(instance == null){
instance = new LazySingleton();
}
}
}
return instance;
}
}
注意:如果使用双重检查锁定实现懒汉式单例类,需要在静态成员变量instance前加修饰符volatile,被volatile修饰的成员变量可以确保多个线程都能正确处理。
饿汉式与懒汉式的比较
Tables | 饿汉式 | 懒汉式 |
---|---|---|
线程安全 | 无需考虑 | |
资源利用率 | 加载时间较长 | 延迟加载 |
单例模式总结
- 主要优点
<1>单例模式提供了对唯一实例的受控访问
<2> 由于在系统内存中只存在一个对象,因此可以节省系统资源
<3>允许可变数目的实例。基于单例模式,开发人员可以进行扩展。(自行提供指定数目实例对象的类可称为多例类) - 主要缺点
<1>由于单例模式中没有抽象层,因此单例类的扩展有很大的困难
<2>单例类的职责过重,在一定程度上违背了单一职责原则。 - 适用场景
<1>系统只需要一个实例对象
<2>客户调用类的单个实例只允许使用一个公共访问点