场景描述
单例模式对于我们来说一点也不模式,是一个常见的名称,单例模式在程序中的实际效果就是:确保一个程序中只有一个实例,并提供一个全局访问点,节省系统资源
单例模式无论是在实际开发中还是在软件应用中比较常见,比如,windows系统的任务管理器、IIS的HttpApplication、实际项目中的日志组件等等
实现方式
单例模式为了实现一个实例,那么只有不把实例创建暴露出去,只通过类本身来创建实例,为了实现效果,需要定义一个私有构造函数
单例模式实现方式有:饿汉式、懒汉式、双重验证式、静态内部类
下面分别对每一种实现方式做一个简单的实例,以及其优缺点
饿汉式
/// <summary> /// 创建一个 Singleton 类(饿汉式) /// 这种方式比较常用,但容易产生垃圾对象。 ///优点:没有加锁,执行效率会提高。 ///缺点:类加载时就初始化,浪费内存。 ///它基于 classloder 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化, ///虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance 方法, ///但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的效果。 /// </summary> public class SingleObject { //创建 SingleObject 的一个对象 private static SingleObject instance = new SingleObject(); //让构造函数为 private,这样该类就不会被实例化 private SingleObject() { Console.WriteLine("我被创建了.饿汉式"); } //获取唯一可用的对象 public static SingleObject GetInstance() { return instance; } public void ShowMessage() { Console.WriteLine("Hello World.饿汉式"); } }
懒汉式
/// <summary> /// 创建一个 Singleton 类(懒汉式) /// 这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。 /// 优点:第一次调用才初始化,避免内存浪费。 /// 缺点:懒汉式在单个线程中没有问题,但多个线程同事访问的时候就可能同事创建多个实例,而且这多个实例不是同一个对象。 /// </summary> public class SingleObject1 { //创建 SingleObject 的一个对象 private static SingleObject1 instance; //让构造函数为 private,这样该类就不会被实例化 private SingleObject1() { } //获取唯一可用的对象 public static SingleObject1 GetInstance() { if (instance == null) { instance = new SingleObject1(); Console.WriteLine("我被创建了.懒汉式"); } return instance; } public void ShowMessage() { Console.WriteLine("Hello World.懒汉式"); } }
双重验证式
/// <summary> /// 创建一个 Singleton 类(双重验证) /// 这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。 /// 优点:第一次调用才初始化,避免内存浪费,线程安全。 /// 缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。 /// </summary> public class SingleObject2 { //创建 SingleObject 的一个对象 private static SingleObject2 instance; // 定义一个标识确保线程同步 private static readonly object locker = new object(); //让构造函数为 private,这样该类就不会被实例化 private SingleObject2() { } //获取唯一可用的对象 public static SingleObject2 GetInstance() { //// 如果为空,那么就加锁,创建实例 if (instance == null) { lock (locker) { //// 枷锁成功后,在做一次非空判断,避免在加锁期间以创建了实例而导致重复创建 if (instance == null) { instance = new SingleObject2(); Console.WriteLine("我被创建了.双重验证"); } } } return instance; } public void ShowMessage() { Console.WriteLine("Hello World.双重验证"); } }
静态内部类
/// <summary> /// 创建一个 Singleton 类(静态内部类) /// 这种方式不用加锁,在效率上和内存使用上都比较优秀 /// 克服了饿汉模式的不足饿汉模式执行效率高,由于在类加载的时候初始化导致内存浪费 /// </summary> public class SingletonStatic { /// <summary> /// 内部类 /// </summary> public class SingletonStaticInner { /// <summary> /// 当一个类有静态构造函数时,它的静态成员变量不会被beforefieldinit修饰 /// 就会确保在被引用的时候才会实例化,而不是程序启动的时候实例化 /// </summary> static SingletonStaticInner() { } /// <summary> /// 实例化 /// </summary> internal static SingletonStatic singletonStatic = new SingletonStatic(); } /// <summary> /// 私有构造函数 /// </summary> private SingletonStatic() { Console.WriteLine("我被创建了.静态内部类"); } /// <summary> /// 获取实例 /// </summary> /// <returns></returns> public static SingletonStatic GetInstance() { return SingletonStaticInner.singletonStatic; } public void ShowMessage() { Console.WriteLine("Hello World.静态内部类"); } }
每一种创建方式测试
创建一个控制台程序,通过多线程对每一种实现方式使用,查看其实例次数分析:
/* 介绍 意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。 主要解决:一个全局使用的类频繁地创建与销毁。 何时使用:当您想控制实例数目,节省系统资源的时候。 如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。 关键代码:构造函数是私有的。 应用实例: 典型的已有应用: 1、windows的任务管理器等 2、IIS的HttpApplication,所有的HttpModule都共享一个HttpApplication实例 在项目中的实际使用场景: 1、日志组件 2、多线程线程池管理 3、网站计数器 4、配置文件管理 */ class Program { static void Main(string[] args) { TaskFactory taskFactory = new TaskFactory(); List<Task> taskList = new List<Task>(); //// 测试--饿汉式 for (int i = 0; i < 5; i++) { taskList.Add(taskFactory.StartNew(() => { SingleObject.GetInstance(); })); } //// 测试--懒汉式 for (int i = 0; i < 5; i++) { taskList.Add(taskFactory.StartNew(() => { SingleObject1.GetInstance(); })); } //// 测试--双重验证 for (int i = 0; i < 5; i++) { taskList.Add(taskFactory.StartNew(() => { SingleObject2.GetInstance(); })); } //// 测试--静态内部类 for (int i = 0; i < 5; i++) { taskList.Add(taskFactory.StartNew(() => { SingletonStatic.GetInstance(); })); } Console.ReadLine(); } }
运行结果:
通过结果可以看出:懒汉式实际创建了2个实例,所以在多线程中,懒汉式有线程不安全问题
总结
根据单例模式是每一种实现方式对比分析,在实际使用过程中:
如果是单线程应用环境,建议可以采用懒汉模
如果是多线程应用环境,建议采用静态内部类方式
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对小牛知识库的支持。
本文向大家介绍C#设计模式之单例模式实例讲解,包括了C#设计模式之单例模式实例讲解的使用技巧和注意事项,需要的朋友参考一下 前言 最近开始花点心思研究下设计模式,主要还是让自己写的代码可重用性高、保证代码可靠性。所谓设计模式,我找了下定义:是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。毫无疑问,设计模式于己于他人于系统都是多赢的;设计模式使代码编制真正工程化;设计模式是软件工
本文向大家介绍JavaScript设计模式之单例模式实例,包括了JavaScript设计模式之单例模式实例的使用技巧和注意事项,需要的朋友参考一下 《Practical Common Lisp》的作者 Peter Seibel 曾说,如果你需要一种模式,那一定是哪里出了问题。他所说的问题是指因为语言的天生缺陷,不得不去寻求和总结一种通用的解决方案。 不管是弱类型或强类型,静态或动态语言,命令式或说
本文向大家介绍C++设计模式之简单工厂模式实例,包括了C++设计模式之简单工厂模式实例的使用技巧和注意事项,需要的朋友参考一下 问题描述 之前在公司做了一个windows 8平台的阅读器。首先,需要将电子书中的内容渲染到屏幕上,而电子书每一页都包含各种各样的内容,比如:图形、图像和文字等等;不同的内容,就是不同的对象;在将不同的内容渲染到屏幕上之前,就需要new操作,建立不同的对象,然后再在屏幕上
本文向大家介绍解析C#设计模式之单例模式,包括了解析C#设计模式之单例模式的使用技巧和注意事项,需要的朋友参考一下 单例模式(Singleton),故名思议就是说在整个应用程序中,某一对象的实例只应该存在一个。比如,一个类加载数据库中的数据到内存中以提供只读数据,这就很适合使用单例模式,因为没有必要在内存中加载多份相同的数据,另外,有些情况下不允许内存中存在多分份相同的数据,比如数据过大,内存
介绍 从本章开始,我们会逐步介绍在JavaScript里使用的各种设计模式实现,在这里我不会过多地介绍模式本身的理论,而只会关注实现。OK,正式开始。 在传统开发工程师眼里,单例就是保证一个类只有一个实例,实现的方法一般是先判断实例存在与否,如果存在直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象。在JavaScript里,单例作为一个命名空间提供者,从全局命名空间里提供一个唯
本文向大家介绍java 设计模式之单例模式,包括了java 设计模式之单例模式的使用技巧和注意事项,需要的朋友参考一下 java 设计模式之单例模式 前言: 在软件开发过程中常会有一些对象我们只需要一个,如:线程池(threadpool)、缓存(cache)、对话框、偏好设置等。这些对象如果制造出多个实例的话可能会导致一些不必要的麻烦,如:程序行为异常、资源使用过量等。这时单例模式就可以确保