创建型-单例模式 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设计模式——单例模式(创建型模式)
概述 单例模式保证对于每一个类加载器,一个类仅有一个实例并且提供全局的访问.其是一种对象创建型模式.对于单例模式主要适用以下几个场景: 系统只需要一个实例对象,如提供一个唯一的序列号生成器 客户调 ...
随机推荐
- 快速串讲——JVM内存的区域划分
目的 快速定位JVM内存泄漏或者溢出等问题. 面试基础题,加分项. 文章持续更新,微信搜索「万猫学社」第一时间阅读,关注后回复「电子书」,免费获取12本Java必读技术书籍. 程序计数器(Progra ...
- Vue(10)表单输入绑定v-model
v-model v-model指定可以实现表单值与属性的双向绑定.即表单元素中更改了值会自动的更新属性中的值,属性中的值更新了会自动更新表单中的值 绑定的属性和事件 v-model在内部为不同的输入元 ...
- js笔记12
1.元素的属性 div.attributes是所有标签属性构成的数组集合 div.classList是所有class名构成的数组集合 在classList的原型链上可以看到add()和remove() ...
- centos 8.3系统调优参数配置
临时设置 最大虚拟内存 [root@Sonnarqube-dev ~]# sysctl -w vm.max_map_count=262144 执行结果 vm.max_map_count = 26214 ...
- Docker Swarm介绍
1 什么是swarmSwarm是Docker公司推出的docker集群管理平台,它将一群Docker主机变成了一台单一的虚拟Docker主机.Swarm使用标准的Docker API接口,因此,任何支 ...
- Docker:docker部署Tomcat运行web项目
查找Docker Hub上的tomcat镜像 docker search tomcat 拉取官方的镜像 docker pull tomcat 启动Tomcat服务 方式1: 通过dockerfile生 ...
- [心得体会]Spring容器的初始化
1. Spring容器的初始化过程 public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) { ...
- idea中IDEA优化配置
进入IDEA 设置.两种方法: 1,File -> Settings 2,工具栏有个工具按钮点下(假如没工具栏,View -> 选下Toolbar) 进入设置页面,从上到下,主要是 外观 ...
- MYSQL数据库数据拆分之分库分表总结 (转)
数据存储演进思路一:单库单表 单库单表是最常见的数据库设计,例如,有一张用户(user)表放在数据库db中,所有的用户都可以在db库中的user表中查到. 数据存储演进思路二:单库多表 随着用户数 ...
- SEO优化:如何挖掘关键词谷歌篇
最近SEO禅在做安卓项目比较忙,大部分入门理论的文章也写了差不多了,有的也写了一个系列,但是感觉还是不够完善,有很多边边角角的地方感觉也没说清楚,所以还是有必要写一些零散文章去补充说明下,就比如关于S ...