单例设计模式
来源:互联网 发布:mac访客模式 编辑:程序博客网 时间:2024/06/05 02:35
定义
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例
使用场景
确保某个类有且只有一个对象,避免产生更多的对象消耗过多的资源。
实现步骤
- 构造函数私有
- 通过一个静态方法或枚举返回该类的实例
- 确保在多线程的情况下,该类的对象有且只有一个
- 确保该类在反序列化时不会重新构建对象
实现方式
1 . 饿汉式
public class Singleton { private static final Singleton mSingleton = new Singleton(); //构造函数私有 private Singleton() {} //对外暴露获取实例的方法 public static Singleton getInstance() { return mSingleton; }}
优缺点:该方式在类加载时就创建实例,需要用到时直接可以获取,并且在多线程中也可以保证有且只有一个实例。缺点也是显而易见的,在获取该实例之前,对象就已经被创建并保存在内存中,耗费了不必要的资源。
2 . 懒汉式
public class Singleton { private static Singleton mSingleton; private Singleton() {} public static synchronized Singleton getInstance() { if(mSingleton == null) { mSingleton = new Singleton(); } return mSingleton; }}
优缺点:可以看到,懒汉式对饿汉式的缺点进行了改进,在类加载的时候不进行初始化,当需要用到该实例时再进行初始化,这样就在一定程度上避免了资源的浪费。
我们可以注意到,在 getInstance() 方法上加了同步锁,这是为了保证在多线程调用的情况下,保证有且只有一个实例。
看似该方式已经很完美了,但是还有一种情况,如果第一次调用后,mSingleton 已经被初始化了,那么后续调用 getInstance() 方法的时候每次都要经过同步锁,这样也会消耗不必要的资源。
3 . Double Check Lock (DCL)
public class Singleton { private static Singleton mSingleton; private Singleton() {} public static Singleton getInstance() { if(mSingleton == null) { synchronized(Singleton.class) { if(mSingleton == null) { mSingleton = new Singleton(); } } } return mSingleton; }}
优缺点:这种方式通过对 mSingleton 的双重判空,弥补了上一种方式的缺陷。第一层判断是为了避免不必要的同步,第二层判断是为了在 mSingleton 为空的时候创建实例。如此,这种方式可以保证在使用时再创建实例,节省了内存的消耗,也避免了因同步造成的资源损耗,是目前最优的单例模式实现方式。这种方式也是最常被使用的。
但是,这种方式也并不是绝对完美的。如果是在高并发的环境下,也可能会出错。接下来我们来分析:
mSingleton = new Singleton() 看上去只是一句代码,实际上它并不具备原子性,这句代码会被编译成多条汇编指令,它大概会进行三个动作
- 给 mSingleton 的实例分配内存
- 调用 Singleton() 的构造函数,进行初始化操作
- 将 mSingleton 对象指向分配的内存空间(该步骤执行后 mSingleton 就不为空了)
由于 Java 编译器允许处理器乱序执行,以及 JDK1.5 之前 Java 内存模型中 Cache 、寄存器到主内存回写顺序规定,上述第二步和第三步的执行顺序是无法确定的。所以,可能会存在这样一种情况,A 线程在执行完上述第三步之后,此时 Singleton 的构造函数还没有执行,数据没有初始化,但是 mSingleton 已经不为空了。此刻 B 线程执行第一层非空判断时,就会直接返回 mSingleton 实例,这时就会出错。
在 JDK1.5 之后,可以给 mSingleton 添加 volatile 关键字,这样就可以保证 mSingleton 对象每次都是从主内存中获取,可以避免以上情况的发生。当然,以上这种出错情况发生的几率很低,如果单例的使用场景比较简单,而且不存在高并发,完全可以忽略这种错误情况。
4 . 静态内部类
public class Singleton { private Singleton() {} public static Singleton getInstance() { return SingletonHolder.mSingleton; } /** * 静态内部类 */ private static class SingletonHolder { private static final Singleton mSingleton = new Singleton(); }}
这种方式也实现了 mSingleton 实例的延迟初始化,并且是线程安全的,所以也是推荐使用的一种方式。
5 . 使用 Map 存储实例
public class SingletonManager { private static Map<String, Object> objMap = new HashMap<String, Object>(); private SingletonManager() {} public static void registerService(String key, Object instance) { if(!objMap.containsKey(key)) { objMap.put(key, instance); } } public static Object getService(String key) { return objMap.get(key); }}
在系统的使用过程中,将不同的单例类的实例存入 Map 中,使用时,通过 key 来获取对应的实例。该方式适用于同一系统中存在多个单例类的情况,可以统一管理,降低耦合度。
以上单例的实现方式还有一个缺陷,就是在反序列化的时候,可能会重新创建实例,为了防止这种情况的发生,我们可以手动返回我们自己创建的 实例,避免了反序列化时默认生成新的实例,可以加入如下方法:
private Object readResolve() throws ObjectStreamException { return mSingleton;}
总结
单例模式,不管以什么样的方式来实现,思想都是一样的。就是构造函数私有化,通过提供公共的静态方法来获取唯一的实例,保证在多线程的情况下实例的唯一性。具体使用哪种方式实现,要根据项目的具体情况来定,以上方式仅供参考。
- 设计模式--单例
- 单例设计模式
- 设计模式----单例
- 单例设计模式
- 单例设计模式
- 单例设计模式
- 单例设计模式
- 单例设计模式
- 单例设计模式
- 单例设计模式
- 设计模式-单例
- 单例设计模式
- 单例设计模式
- 单例设计模式
- 单例设计模式
- 单例设计模式
- 单例设计模式
- 单例设计模式
- 数据结构-->c构建线性表
- Linux CentOS6.7 jdk1.8.0_11 安装Elasticsearch5.0.1
- treeview 使用
- 刷新托盘图标
- 0基础lua学习(十三)Metatable
- 单例设计模式
- HDU-2001(两点距离问题)
- win7 python3.6安装教程及环境配置
- Mybatis入门级教程(一)
- 项目管理中的成本计算
- 一中OJ #1086 最大子矩阵 | 贪心连续子序列 + 平面降维压缩 | 解题报告
- 设计模式:单例模式
- 可控定时任务 框架设计实现
- Android Binder通信