Head First设计模式——单例模式
单例模式是所有设计模式中最简单的模式,也是我们平常经常用到的,单例模式通常被我们应用于线程池、缓存操作、队列操作等等。
单例模式旨在创建一个类的实例,创建一个类的实例我们用全局静态变量或者约定也能办到单例的作用,为什么我们要用单例模式?
接下来我们就从如何形成单例模式,单例模式创建的过程来讲解。
1、单例如何形成
我们平常创建一个对象需要new对象,假如有一个对象ObjectClass我们实例化它。
new ObjectClass()
如果另外一个类要使用ObjectClass则可以再通过new来创建另外一个实例化,如果这个类是public 则我们可以在使用的时候多次实例化对象。
那我们怎么保证类不被其他类实例化,利用private关键字我们可以采用私有构造函数来阻止外部实例化该类。
public class ObjectClass
{
private ObjectClass()
{
}
}
这样一来我们无法实例化ObjectClass则我们就无法使用它。那我们要怎么实例化呢?
由于私有构造方法我们只能在内部访问,所以我们可以用一个内部方法实例化ObjectClass,为了外部能够访问这个方法我们将这个方法设置成static。
这样做了之后确保返回对象始终是第一次创建的对象,我们用一个私有静态对象来存储实例化的对象,如果对象没创建我们则立即创建,如果已经创建就返回已经创建的对象。
public class ObjectClass
{
private static ObjectClass singleton;
private ObjectClass()
{
}
public static ObjectClass GetSingletone()
{
if (singleton == null)
{
singleton = new ObjectClass();
}
return singleton;
}
}
到这里我们的单例模式就形成了,单例模式定义:
单例模式:确保一个类只有一个实例,并提供一个全局访问点。
2、多线程导致单例模式问题
启用多线程测试单例返回对象
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
TestSingleton();
}
Console.ReadKey();
}
public static void TestSingleton()
{
Task.Factory.StartNew(new Action(() =>
{
var hc = ObjectClass.GetSingletone().GetHashCode();
Console.WriteLine(hc);
}));
}
}
如图中做的测试一样,我启了10个线程获得单例对象然后打印对象的HashCode。测试发现有HashCode不一致的情况,证明单例返回的对象并不是只有一个。
因为多线程运行的时候可能会同时进行if (singleton == null)的判断,如果此时singleton变量还没被实例化则可能有多个线程进入到实例化代码,以至于返回的实例化对象不是同一个。
3、解决多线程单例问题
由于多线程导致if检查变量问题,则争对检查问题我们可以有两类解决办法:
①"急切"创建实例,不用延迟实例化做法
急切实例化就是在静态初始化器中创建对象,这样就保证了程序运行阶段单例对象已经创建好,去除if判断。
public class ObjectClass
{
private static ObjectClass singleton=new ObjectClass();
private ObjectClass()
{
}
public static ObjectClass GetSingletone()
{
return singleton;
}
}
②加锁
为了让创建对象只能有一个线程操作,则我们对创建对象代码进行加锁处理,再次改造GetSingletone方法。
public class ObjectClass
{
private static ObjectClass singleton = new ObjectClass();
private static object lockObj = new object();
private ObjectClass()
{
}
public static ObjectClass GetSingletone()
{
lock (lockObj)
{
if (singleton == null)
{
singleton = new ObjectClass();
}
}
return singleton;
}
}
加锁对性能有一定的损耗,如果你的系统对性能要求比较高,我们对于加锁的处理还有一种优化方式:双重检查加锁
public static ObjectClass GetSingletone()
{
if (singleton == null)
{
lock (lockObj)
{
if (singleton == null)
{
singleton = new ObjectClass();
}
}
}
return singleton;
}
使用双重检查加锁,则多线程在运行的时候如果已经创建了单例对象后就不会再进入到lock代码段以此减少锁带来的性能损耗。
然后我们再来测试一波,启用50个线程,可以看到输出的HashCode是一致的。
4、总结
回到我们开始讲的为什么不用全局变量或者约定来解决单例问题,因为对于我们开发来说虽然有约定但是我们不能保证每个人都按照约定或者滥用全局变量造成问题。
而使用单例模式能进行更好的自我约定和管理,当然我们也有可能会滥用单例模式,这就需要对它能解决什么问题如何使用深入理解。
设计模式并不是要生搬硬套,而是在需要的时候符合的场景进行合理使用。
虽然单例模式比较简单,但通过分析我们看到问题也不少,要更好的使用需要我们更好的分析,也希望这篇博文对你有些帮助。
- 动态控制C4C UI元素的显示和隐藏
- 深度学习(deep learning)发展史
- 遗传算法简述
- Spark详解03Job 物理执行图Job 物理执行图
- 干货|Kotlin入门第一课:从对比Java开始
- Spark详解04Shuffle 过程Shuffle 过程
- Spark详解02Job 逻辑执行图Job 逻辑执行图
- Spark详解01概览|Spark部署|执行原理概览Job 例子
- Spark详解05架构Architecture架构
- SQL Server常用命令(平时不用别忘了)
- Spark详解06容错机制Cache 和 Checkpoint Cache 和 Checkpoint
- SQL Server 学习笔记
- Collaborative Filtering(协同过滤)算法详解
- 【Hadoop】三句话告诉你 mapreduce 中MAP进程的数量怎么控制?
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- requests项目实战--抓取百度热搜
- [代码片段]谷歌的一个不错的CSS样式,不得不说大厂的前端设计师美感就是好。
- 罗马数字转整数
- 找出两个文件中相同的单词(java实现)
- Apache日志变量详解
- 三、jQuery中的Ajax
- SAP ABAP ADBC和Java JDBC的使用比较
- 自己开发的一个SAP CRM订单统计工具
- SAP ABAP和Java跨域请求问题的解决方案
- 使用ABAP Channel实现一个订单跟踪工具,提高日常工作效率
- SAP CRM状态字段下拉列表里数据的填充原理
- SAP CRM订单状态管理的一些重要的数据库表
- Angular应用的依赖注入调试
- 给Angular应用增添搜索Search功能
- Angular应用一个创建场景的问题分析