IT评测·应用市场-qidao123.com

标题: 我的设计模式之旅、02 单例模式(第二次更新) [打印本页]

作者: 南七星之家    时间: 2022-9-19 00:50
标题: 我的设计模式之旅、02 单例模式(第二次更新)
编程旅途是漫长遥远的,在不同时刻有不同的感悟,本文会一直更新下去。
思考总结

什么是单例模式

单例模式(Singleton Pattern)属于创建型模式,它提供了一种创建对象的最佳方式。
单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
含义:
注意:
实现方式:
应用实例:
优点:
缺点:
使用场景:
与其他模式的关系:
双重”锁定“

原子操作,是并发编程中”最小的且不可并行化“的操作。本案例中使用原子操作配合互斥锁实现了非常高效的单例模式。互斥锁的代价比普通整数的原子读写高很多,所以在性能敏感的地方添加一个initialized标志位,通过原子检测标志位状态降低互斥锁的次数来提高性能。(也可以使用实例来进行判断,在实例未被创建的时候再加锁处理)
在互斥锁之后还要判断实例是否存在,是因为当多线程的时候,当一个线程处理完退出解锁,另一个在排队等候的线程进入后如果没有实例的判断,那么会再生成一遍实例,没有达到单例的目的。
静态初始化

C# 提供了静态初始化的方法,这种方法可以解决多线程环境下不安全的原因。
C# 给类添加sealed关键字防止子类继承产生多个单例、给静态字段添加readonly修改为只读状态,意味着只能在静态初始化期间或在类构造函数中分配变量。
这种静态初始化的方式是在自己被加载时就将自己实例化,所以被形象地称之为饿汉式单例类,原先的单例模式处理方式是要在被第一次引用时才会自己实例化,这叫懒汉式初始化。
饿汉式初始化是类一加载就实例化的对象,所以要提前占用系统资源。然而懒汉式,又会面临多线程访问的安全性问题,需要做双重锁定才能保证安全。
在golang实现静态初始化,实际上只要把实例化的过程移动到当前文件的init函数中,在包被加载的过程中,会首先运行各个文件的init函数,再运行main函数。
实用类与单例类的比较

在C#经常有工具类之说,这个工具类包含许多静态方法和静态属性。但是这种实用类没有单例类的状态。实用类不能用于继承多台,而单例类虽然实例唯一,但是可以有子类来继承。实用类是一些方法属性的集合,单例是有着唯一对象的实例。
程序介绍


本程序实现了单例模式,三个工作者需要各自找到电梯搭乘!电梯只有一个!
  1. PS C:\Users\小能喵喵喵\Desktop\设计模式\单例模式> go run .
  2. 向海宁 正在搭乘电梯!
  3. 田海彬 正在搭乘电梯!
复制代码
程序代码

singleton.go
  1. package main
  2. import (
  3.         "fmt"
  4.         "sync"
  5.         "sync/atomic"
  6. )
  7. type People string
  8. type Elevator struct {
  9.         passengers map[People]bool
  10. }
  11. var (
  12.         elevator    *Elevator
  13.         initialized uint32
  14.         mu          sync.Mutex
  15. )
  16. // 饿汉式单例
  17. func init() {
  18.         initialized = 1
  19.         elevator = &Elevator{make(map[People]bool)}
  20. }
  21. // 乘客进电梯
  22. func (p People) intoElevator() {
  23.         e := ElevatorGetInstance()
  24.         e.passengers[p] = true
  25. }
  26. // 乘客出电梯
  27. func (p People) outElevator() {
  28.         e := ElevatorGetInstance()
  29.         delete(e.passengers, p)
  30. }
  31. // 乘客按下电梯楼层按钮
  32. func (p People) pressButton() {
  33.         e := ElevatorGetInstance()
  34.         e.start()
  35. }
  36. // 电梯开始运作
  37. func (e *Elevator) start() {
  38.         for k := range e.passengers {
  39.                 fmt.Println(k, "正在搭乘电梯!")
  40.         }
  41. }
  42. // 由于Golang不支持静态方法、静态字段,所以使用纯函数替代面向对象中的静态方法
  43. // 乘客想知道电梯在哪,单例模式
  44. func ElevatorGetInstance() *Elevator {
  45.         if atomic.LoadUint32(&initialized) == 1 {
  46.                 return elevator
  47.         }
  48.         mu.Lock()
  49.         defer mu.Unlock()
  50.         // ^ 懒汉式单例
  51.         if elevator == nil {
  52.                 elevator = &Elevator{make(map[People]bool)}
  53.                 atomic.StoreUint32(&initialized, 1)
  54.         }
  55.         return elevator
  56. }
复制代码
main.go
  1. package main
  2. // 单例模式
  3. // by 小能喵喵喵 2022年9月8日
  4. func main() {
  5.         var workerA, workerB, workerC People = "陈冰", "向海宁", "田海彬"
  6.         workerA.intoElevator()
  7.         workerB.intoElevator()
  8.         workerC.intoElevator()
  9.         // workerA 发现自己电梯坐错了
  10.         workerA.outElevator()
  11.         // workerB 按下了电梯按钮
  12.         workerB.pressButton()
  13. }
复制代码
Console
  1. PS C:\Users\小能喵喵喵\Desktop\设计模式\单例模式> go run .
  2. 向海宁 正在搭乘电梯!
  3. 田海彬 正在搭乘电梯!
复制代码
补充C#单线程单例实现
  1. class Singleton
  2. {
  3.     private static Singleton instance;
  4.     private Singleton() //使用 private 字段外界无法使用new手动创建实例,只能通过静态方法创建
  5.     {   
  6.     }
  7.     public static Singleton GetInstance()
  8.     {
  9.         if(instance == null)
  10.         {
  11.             instance = new Singleton();
  12.         }
  13.         return instance
  14.     }
  15. }
复制代码
所有类都有构造方法,不编码则系统默认生成空的构造方法,若有显示定义的构造方法,默认的构造方法就会失效。
参考资料


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 IT评测·应用市场-qidao123.com (https://dis.qidao123.com/) Powered by Discuz! X3.4