设计模式之单例模式
来源:互联网 发布:商务英语写作 知乎 编辑:程序博客网 时间:2024/04/29 08:00
关于设计模式的学习笔记,教材:《设计模式的艺术之道》 刘伟 著
class TaskManager{ private static TaskManager tm=null; private TaskManager(){ //大量初始化工作 } public static TaskManager getInstance(){ if(tm==null){ tm=new TaskManager(); } return tm; }}
一般的单例模式:
- 将构造函数私有
- 定义静态的TaskManager类型私有成员变量tm
- 增加一个公有的静态方法,返回tm
但这样会出现问题。
比如当多次调用getInstance()方法,而new TaskManager()时因为大量初始化工作需要消耗不少时间时,由于tm尚未创建成功,于是其他调用的getInstance()方法中,tm仍为null,可能会导致创建了多个tm对象。违背了单例模式的初衷。
一开始人们想到两种解决方法:
1.Eager Singleton
在类加载的时候就已经创建单例对象
class TaskManager{ private static final TaskManager tm=new TaskManager(); //其余同上}
2.Lazy Singleton
在类加载时并不实例化,而是在需要时再加载,这种技术又称为Lazy Load(延迟加载技术)。为了避免多个线程同时调用getInstance()方法,在Java中使用关键字synchronized
class TaskManager{ synchronized public static TaskManger getInstance(){ if(tm==null){ tm=new TaskManager(); } return tm; }}
通过增加关键字synchronized进行线程锁定,以处理多个线程同时访问的问题。但是在多线程高并发访问环境下,会导致系统性能大大降低。比如当tm已经创建,而同时调用多次getInstance(),实际上不会并发执行,实际上只需直接return,但要排队。
于是有了这个改进:
class TaskManager{ public static TaskManger getInstance(){ if(tm==null){ synchronized (TaskManager.class){ tm=new TaskManager(); } } return tm; }}
似乎问题解决了,但是如果在某一瞬间,线程A和B都在调用getInstance(),此时均能通过tm==null的判断,那么依然new了两次。因此需要进一步改进。在synchronized 锁定代码中再进行一次tm==null的判断,这称为Double-Check Locking。
class TaskManager{ private volatile static final TaskManager tm=null; public static TaskManger getInstance(){ if(tm==null){ synchronized (TaskManager.class){ if(tm==null){ tm=new TaskManager(); } } } return tm; }}
一开始我有个疑惑,外面的if(tm==null)
是否可以忽略?但仔细一想,如果忽略,那就跟最开始把整个方法都synchronized 差不多了。
这种Double-Check Locking需要在静态成员变量tm添加修饰符volatile,被volatile修饰的成员变量可以确保多个线程都能正确处理。(jdk1.5以上)但是由于volatile关键字会屏蔽Java虚拟机所做的一些代码优化,可能会导致系统运行效率降低,因此即使使用Double-Check Locking依然不好。
比较两种方法
Eager无须考虑多线程访问时的问题,而且因为一开始就实例化好,所以调用速度和反应时间由于Lazy。但是无论系统在运行时是否需要使用该单例对象,在类加载时都创建好了,就会消耗更多系统资源,而且加载时间可能会比较长。
关于类的加载:http://blog.csdn.net/ns_code/article/details/17881581
而Lazy无须一直占用系统资源,但是必须处理好多线程同时访问问题,多线程同时首次引用此类时需要双重检查锁定等机制进行控制,将导致系统性能受到影响。
一种更好的方法
class TaskManager{ private TaskManager(){} private static class HolderClass{ private static final TaskManager tm=new TaskManager(); } public static TaskManger getInstance(){ return HolderClass.tm; }}
类加载时没有实例化TaskManager,而第一次调用getInstance()时加载内部类,初始化其成员变量tm,由Java虚拟机来保证其线程安全性,确保该成员变量只能初始化一次。
这种方法叫Initialization on Deman Holder技术。
通过IoDH,既可以实现Lazy Load,减少系统资源消耗,又保证线程安全,不影响系统性能。缺点是与编程语言本身的特性相关,很多面向对象语言不支持IoDH。
单例模式的优缺点
优点
- 提供了对唯一实例的受控访问
- 节约系统资源
- 允许可变数目的实例
缺点
- 没有抽象层,类的拓展有很大困难
- 职责过重,即提供业务方法,又提供了创建对象的方法,即既有对象的创建,也有对象的使用
- 很多语言如Java、C#的运行环境都提供了自动垃圾回收技术,因此,如果实例化的单例对象长时间不被使用,系统会认为是垃圾,自动销毁并回收。下次使用时重新实例化,这将导致共享的实力对象状态丢失。
适用场景
- 系统只需要一个实例对象
- 客户调用类的单个实例只允许使用一个公共访问点。
- 设计模式之 单例设计模式
- 设计模式之 单例设计模式
- 设计模式之单例设计模式
- 设计模式之-----------单例设计模式
- 设计模式之:单例设计模式
- 设计模式之单例设计模式
- 设计模式之单例设计模式
- 设计模式之单例设计模式
- 设计模式之单例设计模式
- 设计模式之单例设计模式
- 设计模式之单例设计模式
- 设计模式之单例设计模式
- 设计模式之单例设计模式
- 设计模式之-单例设计模式
- 设计模式之单例设计模式 标签: 设计模式
- 设计模式之单例
- 设计模式之单例
- 设计模式之 单例
- TIM_SelectOutputTrigger()函数的作用是干嘛的?
- unix基本命令
- Http的请求的全过程
- 操作系统常见的几个进程调度算法
- 机器学习和数据挖掘快问快答
- 设计模式之单例模式
- Android 应用安全
- NDK开发 - JNI数组数据处理
- pd.read_excel()文档翻译图片
- TreeGrid节点数组与层级Object的Json格式数据互转
- Glide配合PhotoView使用时,图片放大缩小时宽高显示出问题解决记录(新更新)
- sql server查看外键。
- 链表中倒数第k个结点
- java实现双栈计算整数表达式