单例模式 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的更多相关文章

  1. Python设计模式 - 创建型 - 单例模式(Singleton) - 十种

    对于很多开发人员来说,单例模式算是比较简单常用.也是最早接触的设计模式了,仔细研究起来单例模式似乎又不像看起来那么简单.我们知道单例模式适用于提供全局唯一访问点,频繁需要创建及销毁对象等场合,的确方便 ...

  2. 单例模式——创建型模式01

    1. 名称     单例模式(Singleton Pattern):确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类.单例模式是一种对象创建型模式. 2. 问题    ...

  3. 设计模式学习之单例模式(Singleton,创建型模式)(4)

    假如程序中有一个Person类,我的需求就是需要在整个应用程序中只能new一个Person,而且这个Person实例在应用程序中进行共享,那么我们该如何实现呢? 第一步: 新建一个Person类,类中 ...

  4. C#设计模式之一单例模式(Singleton Pattern)【创建型】

    一.引言     看了李建忠老师的讲的设计模式已经有一段时间了(这段时间大概有一年多了),自己还没有写过自己的.有关设计模式的文章.这次想写一些关于设计模式的文章,用自己的理解和代码来写,算是复习一遍 ...

  5. [19/04/22-星期一] GOF23_创建型模式(单例模式)

    一.概念 <Design Patterns: Elements of Reusable Object-Oriented Software>(即后述<设计模式>一书),由 Eri ...

  6. 设计模式01: Singleton 单例模式(创建型模式)

    Singleton 单例模式(创建型模式) 动机(Motivation)当进行软件开发是会有这样一种需求:在系统中只有存在一个实例才能确保它们的逻辑正确性.以及良好的效率.这应该是类设计者的责任,而不 ...

  7. java架构之路-(设计模式)五种创建型模式之单例模式

    设计模式自身一直不是很了解,但其实我们时刻都在使用这些设计模式的,java有23种设计模式和6大原则. 设计模式是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可 ...

  8. Java设计模式 - 单例模式(创建型模式)

    单例模式我在上学期看一些资料时候学习过,没想到这学期的软件体系结构就有设计模式学习,不过看似篇幅不大,介绍得比较简单,在这里我总结下单例模式,一来整理之前的笔记,二来也算是预习复习课程了. 概述 单例 ...

  9. Java设计模式——单例模式(创建型模式)

    概述   单例模式保证对于每一个类加载器,一个类仅有一个实例并且提供全局的访问.其是一种对象创建型模式.对于单例模式主要适用以下几个场景: 系统只需要一个实例对象,如提供一个唯一的序列号生成器 客户调 ...

随机推荐

  1. String ,StringBuffer 与S tringBuilder的区别??

    String 字符串常量StringBuffer 字符串变量(线程安全)StringBuilder 字符串变量(非线程安全) ------------------------------------- ...

  2. [.NET大牛之路 002] 什么是 .NET

    .NET 是一个开发平台,或者叫开发者平台.使用 .NET 你可以创建不同类型的应用程序,使用多种开发语言.编辑器和工具库创建网页.手机.桌面以及游戏等应用.其核心特点是:免费.开源和跨平台. 语言和 ...

  3. Unity StateMachineBehaviour

    在unity animator中单个Animator Clip中点击Add Behaviour增加当执行该动画时的一些状态代码,请看如下 创建完之后基本代码结构如下:(如果想修改默认代码结构,请看示例 ...

  4. kubernetes的存活探针和就绪探针

    1.存活探针 使用Kubernetes的一个主要好处是,可以给Kubernetes-个容器列表来由其保持容器在集群中的运行.可以通过让Kubernetes创建pod资源,为其选择一个工作节点并在该节点 ...

  5. 关于 Index '8' specified is out of bounds.

    报类似这样的错误暂时我只发现了两个原因: 1, 数组超出了界线,这个自己多多注意,加判断,在循环的时候看看是不是有结束条件 2, 你需要提交的网页不存在.有可能是因为你没有这个文件.可能是你的文件名错 ...

  6. Raspberry Pi:树莓派安装Kali2021新版本

    准备材料 树莓派4B kali系统镜像 SDFormatter (格式化工具) Win32DiskImager (镜像拷录工具) 镜像下载 kali下载地址:https://www.offensive ...

  7. redis集群搭建中遇到的一些问题

    redis单机模式启动后,修改完配置文件,使用以下命令创建redis集群: sudo ./src/redis-trib.rb create --replicas 1 ip1:6379 ip2:6379 ...

  8. Spring:Spring-IOC简介

    想要了解控制反转( Inversion of Control ), 我觉得有必要先了解软件设计的一个重要思想:依赖倒置原则(Dependency Inversion Principle ). 什么是依 ...

  9. php结合redis实现高并发下的抢购、秒杀功能 (转)

      抢购.秒杀是如今很常见的一个应用场景,主要需要解决的问题有两个: 1 高并发对数据库产生的压力 2 竞争状态下如何解决库存的正确减少("超卖"问题) 对于第一个问题,已经很容易 ...

  10. androidstudio创建第一个so文件

    前言:之前看安卓软件安全与逆向分析这书,看到ndk开发这节,发现自己连so文件都没编译操作过233,所以就直接上手试试, 感觉挺好玩的,把关键的加密流程都放进so中去实现,这周先写个demo试试,感觉 ...