23种设计模式之单例(Singleton Pattern)
单例
在软件系统中,经常有这样一些特殊的类,必须保证它们在系统中只存在一个实例(eg:应对一些特殊情况,比如数据库连接池(内置了资源) 全局唯一号码生成器),才能确保它们的逻辑正确性、以及良好的效率。
优点:单例的好处就是单例,就是全局唯一的一个实例
单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例
缺点:单例可以避免重复创建,但是也会常驻内存 除非是真的有必要,否则不要单例
使用单例对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用new关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类
如何绕过常规的构造器,提供一种机制来保证一个类只创建一个实例?
如何实现?将构造函数私有化,然后对外提供一个公开的静态方法,使用一个静态属性进行判断当前对象是否被创建
// 不要用这种方式
public class Singleton
{
private static Singleton _instance = null;
private Singleton() { }
public static Singleton CreateInstance()
{
if (_instance == null)
{
_instance = new Singleton();
}
return _instance;
}
}
上面的方法是非线程安全的,多个不同的线程可以同时进入这个方法,如果instance为空的并且这里返回真的情况下,都可以创建实例,这显然违反了单例模式,实际上,在测试以前,实例就已经有可能被创建了,但是内存模型不能保证这个实例能被其他的线程看到,除非合适的内存屏障已经被跨过了
我们把上面的代码优化一下
/// <summary>
/// 单例类:一个构造对象很耗时耗资源类型
/// 懒汉式单例模式
/// </summary>
public class Singleton
{
/// <summary>
/// 构造函数耗时耗资源
/// </summary>
private Singleton()
{
long lResult = ;
for (int i = ; i < ; i++)
{
lResult += i;
}
Thread.Sleep();
Console.WriteLine("{0}被构造一次", this.GetType().Name);
}
/// <summary>
/// 3 全局唯一静态 重用这个变量
/// </summary>
private static volatile Singleton _Singleton = null;
//volatile 促进线程安全 让线程按顺序操作
private static readonly object Singleton_Lock = new object();
/// <summary>
/// 2 公开的静态方法提供对象实例
/// </summary>
/// <returns></returns>
public static Singleton CreateInstance()
{
if (_Singleton == null)//是_Singleton已经被初始化之后,就不要进入锁等待了
{
lock (Singleton_Lock)
//保证任意时刻只有一个线程进入lock范围
//也限制了并发,尤其是_Singleton已经被初始化之后
{
//Thread.Sleep(1000);
//Console.WriteLine("等待锁1s之后才继续。。。");
if (_Singleton == null)//保证只实例化一次
{
_Singleton = new Singleton();
}
}
}
return _Singleton;
} //既然是单例,大家用的是同一个对象,用的是同一个方法,那还会并发吗 还有线程安全问题吗?
public int iTotal = ;
public void Show()
{
//lock (Singleton_Lock)
//{
this.iTotal++;
//}
} public static void Test()
{
Console.WriteLine("Test1");
Console.WriteLine(_Singleton.iTotal);
} }
前端调用
{
List<Task> tasks = new List<Task>();
for (int i = ; i < ; i++)
{
tasks.Add(Task.Run(() =>
{
Singleton singleton = Singleton.CreateInstance();
singleton.Show();
}));
}
Task.WaitAll(tasks.ToArray());
Singleton.Test();
//iTotal 是0 1 10000 还是其他的
//其他值,1到10000范围内都可能 线程不安全
}
运行代码我们会发现一个问题
iTotal 是0 1 10000 还是其他的,
其他值,1到10000范围内都可能 线程不安全
为什么呢?造成这种情况的原因单例执行singleton.Show()方法时 iTotal在等于某个值时被附加多次,由此得到结论:
即使是单例,变量也不是线程安全的,单例不是为了保证线程安全
如何优化?给show方法加把锁
public void Show()
{
lock (Singleton_Lock)
{
this.iTotal++;
}
}
单例还有另外的写法,以上是懒汉式单例模式,下面我们来看看饿汉式
利用静态构造函数 程序第一次使用这个类型前被调用,且只调用一次
/// <summary>
/// 单例类:一个构造对象很耗时耗资源类型
///
/// 饿汉式
/// </summary>
public class SingletonSecond
{
/// <summary>
/// 1 构造函数耗时耗资源
/// </summary>
private SingletonSecond()
{
long lResult = ;
for (int i = ; i < ; i++)
{
lResult += i;
}
Thread.Sleep();
Console.WriteLine("{0}被构造一次", this.GetType().Name);
}
/// <summary>
/// 静态构造函数:由CLR保证,程序第一次使用这个类型前被调用,且只调用一次
///
/// 检测,初始化
/// 写日志功能的文件夹检测
/// XML配置文件
/// </summary>
static SingletonSecond()
{
_SingletonSecond = new SingletonSecond();
Console.WriteLine("SingletonSecond 被启动");
} private static SingletonSecond _SingletonSecond = null;
public static SingletonSecond CreateInstance()
{
return _SingletonSecond;
}//饿汉式 只要使用类就会被构造 }
另外一种类似的,利用静态字段创建对象
/// <summary>
/// 单例类:一个构造对象很耗时耗资源类型
/// 饿汉式
/// </summary>
public class SingletonThird
{
/// <summary>
/// 构造函数耗时耗资源
/// </summary>
private SingletonThird()
{
long lResult = ;
for (int i = ; i < ; i++)
{
lResult += i;
}
Thread.Sleep();
Console.WriteLine("{0}被构造一次", this.GetType().Name);
} /// <summary>
/// 静态字段:在第一次使用这个类之前,由CLR保证,初始化且只初始化一次
/// 这个比今天构造函数还早
/// </summary>
private static SingletonThird _SingletonThird = new SingletonThird();//打印个日志
public static SingletonThird CreateInstance()
{
return _SingletonThird;
}//饿汉式 只要使用类就会被构造 public void Show()
{
Console.WriteLine("这里是{0}.Show", this.GetType().Name);
} }
本文参考文档:https://csharpindepth.com/Articles/Singleton#unsafe
23种设计模式之单例(Singleton Pattern)的更多相关文章
- 23种设计模式 - 对象性能(Singleton - Flyweight享元)
其他设计模式 23种设计模式(C++) 每一种都有对应理解的相关代码示例 → Git原码 ⌨ 对象性能 面向对象很好地解决了"抽象"的问题,但是必不可免地付出一定的代价.对于通常情 ...
- java23种设计模式之一: 单例模式(Singleton Pattern)
单例模式(Singleton Pattern)是设计模式中比较常用的一种,下面来总结单例模式的知识,包括: 1.理解什么是单例模式.单例模式有什么优点/缺点.单例模式的应用场景: 2.再来看看Java ...
- 两种设计模式(2)==>>"单例"
所谓“单例”: 单例模式是一种常用的软件设计模式.在它的核心结构中只包含一个被称为单例的特殊类.通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资 ...
- 【Unity3D与23种设计模式】单例模式(Singleton)
GoF中定义: "确认类只有一个对象,并提供一个全局的方法来获取这个对象" 使用单例模式的前提 它只能产生一个对象且不能够被继承 单例模式的优点: 提供方便获取唯一对象的方法 可以 ...
- 23种设计模式之单例模式(Singleton)
单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法. public class SingleTon { private static Si ...
- Java设计模式之 — 单例(Singleton)
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/8860649 写软件的时候经常需要用到打印日志功能,可以帮助你调试和定位问题,项目上 ...
- oop的三种设计模式(单例、工厂、策略)
参考网站 单例模式: 废话不多说,我们直接上代码: <?php /** 三私一公 *私有的静态属性:保存类的单例 *私有的__construct():阻止在类的外部实例化 *私有的__clone ...
- 23种设计模式之观察者模式(Observer Pattern)
观察者模式(Observer Pattern):定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主体对象,这个主题对象在状态发生变化时,会通知所有观察者.当一个对象改变需要同时改变其他对象, ...
- 23种设计模式之适配器模式(Adapter Pattern)
适配 即在不改变原有实现的基础上,将原先不兼容的接口转换为兼容的接口.例如:二转换为三箱插头,将高电压转换为低电压等. 动机(Motivate): 在软件系统中,由于应用环境的变化,常常需要将“ ...
随机推荐
- Egret白鹭开发微信小游戏程序跳转功能(由一个小游戏跳转到另一个小游戏)
假设我们要实现的功能是从小游戏A跳转到小游戏B 对于小游戏A: (1)在platform.ts中添加代码如下: /** * 平台数据接口. * 由于每款游戏通常需要发布到多个平台上,所以提取出一个统一 ...
- 使用golang对海康sdk进行业务开发
目录 准备工作 开发环境信息 改写HCNetSDK.h头文件 开发过程 基本数据类型转换 业务开发 参考 项目最近需要改造升级:操作海康摄像头(包括登录,拍照,录像)等基本功能.经过一段时间研究后,发 ...
- 深度好文,springboot启动原理详细分析
我们开发任何一个Spring Boot项目,都会用到如下的启动类 1 @SpringBootApplication 2 public class Application { 3 public stat ...
- hdu 5977 Garden of Eden(点分治+状压)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5977 题解:这题一看就知道是状压dp然后看了一下很像是点分治(有点明显)然后就是简单的点分治+状压dp ...
- codeforces 816 D. Karen and Test(逆元+思维+组合数)
题目链接:http://codeforces.com/contest/816/problem/D 题解:显然一看到这题应该会想到是有什么规律的于是多写几项就会发现偶数列之间是有关系的. 满足a[i][ ...
- ACM-ICPC 2017 Asia Urumqi:A. Coins(DP) 组合数学
Alice and Bob are playing a simple game. They line up a row of nn identical coins, all with the head ...
- codeforces 454 E. Little Pony and Summer Sun Celebration(构造+思维)
题目链接:http://codeforces.com/contest/454/problem/E 题意:给出n个点和m条边,要求每一个点要走指定的奇数次或者是偶数次. 构造出一种走法. 题解:可能一开 ...
- .net core Webapi +EF
开发工具 Vs2017 +MSsqlsever 打开VS2017,新建web项目 点击确认,生成项目,在项目中增加文件夹Model,在Model中增加类TodoItem public class To ...
- HTML5基本介绍
HTML5简介 HTML是互联网上应用最广泛的标记语言.HTML文件就是普通文本+HTML标记,而不同的HTML标记能表示不同的效果.(简单的说HTML是超文本标记语言) HTML5草案的前身名为 W ...
- NOIP2012 D2 T3 疫情控制 洛谷P1084
题目链接:https://www.luogu.org/problemnew/show/P1084 算法:倍增,二分答案,贪心 + 瞎搞.. 背景:上学长的数论课啥也听不懂,于是前去提高组找安慰.不巧碰 ...