创建型-单例模式 SingletonPattern
单例模式 Singleton
- 保证一个类只有一个实例的实现方法
- 给其他类提供一个全局的访问点。
- 由自己创建自己的唯一实例
实现
- 实现方法分为饿汉式(线程安全)、懒汉式(线程不安全)、懒汉式(lock+双重验证、线程安全)、延迟加载(Lazy、线程安全)
1.饿汉式
这种方式比较常用,但容易产生垃圾对象.这时候初始化 instance 显然没有达到 lazy loading 的效果。
优点:没有加锁,执行效率会提高。
缺点:类加载时就初始化,浪费内存。
public class EagerSingleton
{
private EagerSingleton() { }
private static readonly EagerSingleton Instance = new EagerSingleton();
public static EagerSingleton GetInstance()
{
return Instance;
}
}
2.最简单的实现:懒汉式(线程不安全)
namespace Singleton
{
public class Singleton
{
private static Singleton _uniqueInstance;
private Singleton()
{
}
public static Singleton GetInstance()
{
if (_uniqueInstance is null)
{
_uniqueInstance = new Singleton();
}
return _uniqueInstance;
}
}
}
定义了一个静态方法,作为全局访问点,在单线程下是正常的,在多线程同时运行GetInstance,得到的_uniqueInstance都是null,此时就会创建多个 定义了一个静态方法,作为全局访问点,在单线程下是正常的,在多线程同时运行GetInstance,得到的_uniqueInstance都是null,此时就会创建多个的实例。
多线程访问得到hash code是不一样的。
static void Main(string[] args)
{
Task.Run(() =>
{
Singleton singleton = Singleton.GetInstance();
Console.WriteLine(singleton.GetHashCode());
});
Task.Run(() =>
{
Singleton singleton = Singleton.GetInstance();
Console.WriteLine(singleton.GetHashCode());
});
Console.WriteLine("over!");
}
over还提前输出。
over!
4032828
6044116
static void Main(string[] args)
{
Singleton singleton1 = Singleton.GetInstance();
Console.WriteLine(singleton1.GetHashCode());
Singleton singleton2 = Singleton.GetInstance();
Console.WriteLine(singleton2.GetHashCode());
Console.WriteLine("over!");
}
输出
58225482
58225482
over!
3.懒汉式(lock+双重验证、线程安全)
lock关键字
MSDN介绍
lock 关键字可确保当一个线程位于代码的临界区时,另一个线程不会进入该临界区。 如果其他线程尝试进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放。
lock 关键字在块的开始处调用 Enter,而在块的结尾处调用 Exit。 ThreadInterruptedException 引发,如果 Interrupt 中断等待输入 lock 语句的线程。
通常,应避免锁定 public 类型,否则实例将超出代码的控制范围。
常见的结构 lock (this)、lock (typeof (MyType)) 和 lock ("myLock") 违反此准则:
如果实例可以被公共访问,将出现 lock (this) 问题。
如果 MyType 可以被公共访问,将出现 lock (typeof (MyType)) 问题。
由于进程中使用同一字符串的任何其他代码都将共享同一个锁,所以出现 lock("myLock") 问题。
最佳做法是定义 private 对象来锁定, 或 private static 对象变量来保护所有实例所共有的数据。
在 lock 语句的正文不能使用 等待 关键字。
最常使用的锁是如下格式的代码段:
private static object objlock = new object();
lock (objlock )
{
//要执行的代码逻辑
}
使用lock关键字解决多线程问题
public static LockSingleton GetInstance()
{
// 当第一个线程运行到这里时,此时会对locker对象 "加锁",
// 当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁
// lock语句运行完之后(即线程运行完之后)会对该对象"解锁"
lock (Locker)
{
// 如果类的实例不存在则创建,否则直接返回
if (_uniqueInstance == null)
{
_uniqueInstance = new LockSingleton();
}
}
return _uniqueInstance;
}
在lock之前判断是否实例
上面的代码还可以优化,通过判断对象是否为null,如果不是null,则直接返回,否则先锁,然后再生成实例,保证不同线程访问得到的是一个实例
public static LockSingleton GetInstance()
{
// 双重锁定只需要一句判断就可以了
if (_uniqueInstance == null)
{
lock (Locker)
{
if (_uniqueInstance == null)
{
_uniqueInstance = new LockSingleton();
}
}
}
return _uniqueInstance;
}
3.使用lock
Task.Run(() =>
{
LockSingleton lockSingleton = LockSingleton.GetInstance();
Console.WriteLine(lockSingleton.GetHashCode());
});
Task.Run(() =>
{
LockSingleton lockSingleton = LockSingleton.GetInstance();
Console.WriteLine(lockSingleton.GetHashCode());
});
输出结果
over!
6044116
6044116
延迟加载(Lazy)
public class LazySingleton
{
private static readonly Lazy<LazySingleton> SingletonLazy = new Lazy<LazySingleton>(() => new LazySingleton());
/// <summary>
/// 私有构造函数
/// </summary>
private LazySingleton()
{
Console.WriteLine("我被创建了.Lazy");
}
/// <summary>
/// 获取实例
/// </summary>
/// <returns></returns>
public static LazySingleton GetInstance()
{
return SingletonLazy.Value;
}
}
总结
单例主要分为如下几种方式,在实际使用过程中:建议采用延迟加载(Lazy)
饿汉式 | 懒汉式 | 懒汉式+lock锁+双重判断 | 延迟加载(Lazy) |
---|---|---|---|
线程安全 | 线程不安全 | 线程安全 | 线程安全 |
不是延迟加载(会浪费内存) | 会延迟加载 | 会延迟加载 | 会延迟加载 |
没有加锁 | 没有加锁 | 加锁 | 加锁 |
创建型-单例模式 SingletonPattern的更多相关文章
- Python设计模式 - 创建型 - 单例模式(Singleton) - 十种
对于很多开发人员来说,单例模式算是比较简单常用.也是最早接触的设计模式了,仔细研究起来单例模式似乎又不像看起来那么简单.我们知道单例模式适用于提供全局唯一访问点,频繁需要创建及销毁对象等场合,的确方便 ...
- 单例模式——创建型模式01
1. 名称 单例模式(Singleton Pattern):确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类.单例模式是一种对象创建型模式. 2. 问题 ...
- 设计模式学习之单例模式(Singleton,创建型模式)(4)
假如程序中有一个Person类,我的需求就是需要在整个应用程序中只能new一个Person,而且这个Person实例在应用程序中进行共享,那么我们该如何实现呢? 第一步: 新建一个Person类,类中 ...
- C#设计模式之一单例模式(Singleton Pattern)【创建型】
一.引言 看了李建忠老师的讲的设计模式已经有一段时间了(这段时间大概有一年多了),自己还没有写过自己的.有关设计模式的文章.这次想写一些关于设计模式的文章,用自己的理解和代码来写,算是复习一遍 ...
- [19/04/22-星期一] GOF23_创建型模式(单例模式)
一.概念 <Design Patterns: Elements of Reusable Object-Oriented Software>(即后述<设计模式>一书),由 Eri ...
- 设计模式01: Singleton 单例模式(创建型模式)
Singleton 单例模式(创建型模式) 动机(Motivation)当进行软件开发是会有这样一种需求:在系统中只有存在一个实例才能确保它们的逻辑正确性.以及良好的效率.这应该是类设计者的责任,而不 ...
- java架构之路-(设计模式)五种创建型模式之单例模式
设计模式自身一直不是很了解,但其实我们时刻都在使用这些设计模式的,java有23种设计模式和6大原则. 设计模式是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可 ...
- Java设计模式 - 单例模式(创建型模式)
单例模式我在上学期看一些资料时候学习过,没想到这学期的软件体系结构就有设计模式学习,不过看似篇幅不大,介绍得比较简单,在这里我总结下单例模式,一来整理之前的笔记,二来也算是预习复习课程了. 概述 单例 ...
- Java设计模式——单例模式(创建型模式)
概述 单例模式保证对于每一个类加载器,一个类仅有一个实例并且提供全局的访问.其是一种对象创建型模式.对于单例模式主要适用以下几个场景: 系统只需要一个实例对象,如提供一个唯一的序列号生成器 客户调 ...
随机推荐
- 向虚拟机注册钩子,实现Bean对象的初始化和销毁方法
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 有什么方式,能给代码留条活路? 有人说:人人都是产品经理,那你知道吗,人人也都可以是 ...
- 感知机与支持向量机 (SVM)
感知机与SVM一样都是使用超平面对空间线性可分的向量进行分类,不同的是:感知机的目标是尽可能将所有样本分类正确,这种策略指导下得出的超平面可能有无数个,然而SVM不仅需要将样本分类正确,还需要最大化最 ...
- 555定时器(1)单稳态触发器电路及Multisim实例仿真
555定时器(Timer)因内部有3个5K欧姆分压电阻而得名,是一种多用途的模数混合集成电路,它能方便地组成施密特触发器.单稳态触发器与多谐振荡器,而且成本低,性能可靠,在各种领域获得了广泛的应用. ...
- 我是如何用 ThreadLocal 虐面试官的?
我是陈皮,一个在互联网 Coding 的 ITer,微信搜索「陈皮的JavaLib」第一时间阅读最新文章,回复[资料],即可获得我精心整理的技术资料,电子书籍,一线大厂面试资料和优秀简历模板. Thr ...
- 使用阿里云服务器部署jupyter notebook远程访问
安装annaconda 与jupyter notebook annaconda在已经自带了jupyter notebook.jupyter lab.ipython 等一系列工具,不需要再单独安装这些工 ...
- Kubernetes之deployment
Kubernetes实现了零停机的升级过程.升级操作可以通过使用ReplicationController或者ReplicaSet实现,但是Kubernetes提供了另一种基于ReplicaSet的资 ...
- 3shell命令替换
Shell 命令替换是指将命令的输出结果赋值给某个变量.比如,将使用ls命令查看到的某个目录中的内容保存到某个变量中,这就需要使用命令替换. Shell 中有两种方式可以完成命令替换,一种是反引号` ...
- C语言:常用数学函数
#include <stdio.h> #include <math.h> #include <stdlib.h> #include <time.h> # ...
- 安卓源码默认开启USB调试
找到\frameworks\base\services\usb\java\com\android\server\usb\UsbDeviceManager.java下的 Settings.Global. ...
- SpringBoot自动装配-自定义Start
SpringBoot自动装配 在没有使用SpringBoot之前,使用ssm时配置redis需要在XML中配置端口号,地址,账号密码,连接池等等,而使用了SpringBoot后只需要在applicat ...