Go 语言实现创建型设计模式 - 单例模式

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: Go 语言实现创建型设计模式 - 单例模式

介绍

单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点。

因为它同时解决了两个问题,所以它违反了单一职责原则。

使用场景

什么场景适合使用单例模式呢?

某个类对于所有客户端只有一个可用的实例

比如记录应用程序的运行日志,因为记录日志的文件只有一个,所以只能有一个日志类的实例向日志文件中写入,否则会出现日志内容互相覆盖的问题。

需要更加严格地控制全局变量

所谓更加严格地控制全局变量,即使用单例模式确保一个类只有一个实例,除了该类自己以外,无法通过任何方式替换缓存的实例(控制全局变量)。

实现方式

在 Go 语言中,没有类 Class 的概念,我们可以使用结构体 struct 替代。

  1. 定义一个私有变量,用于保存单例类的实例。
  2. 定义一个公有函数,用于获取单例类的实例。
  3. 在公有函数中实现 “延迟实例化”。

04

Go 实现

实现单例模式,一般分为三种方式,分别是急切实例化(饿汉式)、延迟实例化(懒汉式)和双重检查加锁实例化。

此外,Go 标准库 sync/once,也可用于实现单例模式。

急切实例化:

急切实例化(饿汉式)是指在导入包时自动创建实例,并且创建的实例会一直存储在内存中,它是并发安全的,可以使用 init() 初始化函数实现。

一般用于实例占用资源少,并且使用频率高的场景。

type singletonV1 struct {
}
var instance *singletonV1
func init() {
 instance = new(singletonV1)
 fmt.Printf("%p\n", instance)
}
func GetInstance() *singletonV1 {
 return instance
}

延迟实例化:

延迟实例化(懒汉式)是指在导入包时不自动创建实例,而是在初次使用时,才会创建实例。它不是并发安全的,可以通过加锁确保协程的并发安全,但是会影响程序的性能。

一般用于实例占用资源多,并且使用率低的场景。

非并发安全:

type singletonV2 struct {
}
var instance *singletonV2
func GetInstance() *singletonV2 {
 if instance == nil {
  instance = new(singletonV2)
 }
 fmt.Printf("%p\n", instance)
 return instance
}

并发安全:

type singletonV3 struct {
}
var instance *singletonV3
var mu sync.Mutex
func GetInstance() *singletonV3 {
 if instance == nil {
  mu.Lock()
  defer mu.Unlock()
  instance = new(singletonV3)
 }
 fmt.Printf("%p\n", instance)
 return instance
}

双重检查加锁实例化:

双重检查加锁实例化实际上是对通过锁支持并发的延迟实例化的优化,减少锁操作,降低性能损耗。

type singletonV4 struct {
}
var instance *singletonV4
var mu sync.Mutex
func GetInstance() *singletonV4 {
 if instance == nil {
  mu.Lock()
  defer mu.Unlock()
  if instance == nil {
   instance = new(singletonV4)
  }
 }
 fmt.Printf("%p\n", instance)
 return instance
}

阅读上面这段代码,第一次 nil 判断,是为了减少锁操作,第二次 nil 判断,是为了确保只有一个争抢到锁的协程创建实例。

双重检查加锁实例化(原子操作):

双重检查加锁实例化的两次检查,我们还可以将第一次 nil 判断,改为通过使用 sync/atomic 包的原子操作判断,决定是否需要进行争抢锁。

type singletonV5 struct {
}
var instance *singletonV5
var mu sync.Mutex
var done uint32
func GetInstance() *singletonV5 {
 if atomic.LoadUint32(&done) == 0 {
  mu.Lock()
  defer mu.Unlock()
  if instance == nil {
   defer atomic.StoreUint32(&done, 1)
   instance = new(singletonV5)
  }
 }
 fmt.Printf("%p\n", instance)
 return instance
}

sync/once:

我们在介绍 Go 语言并发的文章中,了解到 sync/once 包的 Do() 方法可以确保只执行一次。

type singletonV6 struct {
}
var instance *singletonV6
var once sync.Once
func GetInstance() *singletonV6 {
 once.Do(func() {
  instance = new(singletonV6)
 })
 fmt.Printf("%p\n", instance)
 return instance
}

我们可以通过阅读 sync/once 包的源码发现,实际上 Do() 方法也是使用了 sync/atomic 包的 StoreUint32 方法和 LoadUint32() 方法。

05

总结

本文我们介绍了创建型设计模式-单例模式,并且介绍了几种 Go 实现方式。

需要注意的是,我们在高并发场景中,需要考虑并发安全的问题。

推荐阅读:

参考资料:

  1. https://en.wikipedia.org/wiki/Software_design_pattern
  2. https://en.wikipedia.org/wiki/Design_Patterns
相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
打赏
0
1
1
0
8
分享
相关文章
设计模式2:单例模式
单例模式是一种创建型模式,确保一个类只有一个实例,并提供全局访问点。分为懒汉式和饿汉式: - **懒汉式**:延迟加载,首次调用时创建实例,线程安全通过双重检查锁(double check locking)实现,使用`volatile`防止指令重排序。 - **饿汉式**:类加载时即创建实例,线程安全但可能浪费内存。 示例代码展示了如何使用Java实现这两种模式。
14 4
|
3天前
|
公司局域网管理系统里的 Go 语言 Bloom Filter 算法,太值得深挖了
本文探讨了如何利用 Go 语言中的 Bloom Filter 算法提升公司局域网管理系统的性能。Bloom Filter 是一种高效的空间节省型数据结构,适用于快速判断元素是否存在于集合中。文中通过具体代码示例展示了如何在 Go 中实现 Bloom Filter,并应用于局域网的 IP 访问控制,显著提高系统响应速度和安全性。随着网络规模扩大和技术进步,持续优化算法和结合其他安全技术将是企业维持网络竞争力的关键。
17 1
公司局域网管理系统里的 Go 语言 Bloom Filter 算法,太值得深挖了
|
10天前
|
【02】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-2月12日优雅草简化Centos stream8安装zabbix7教程-本搭建教程非docker搭建教程-优雅草solution
【02】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-2月12日优雅草简化Centos stream8安装zabbix7教程-本搭建教程非docker搭建教程-优雅草solution
62 20
探秘员工泄密行为防线:基于Go语言的布隆过滤器算法解析
在信息爆炸时代,员工泄密行为对企业构成重大威胁。本文聚焦布隆过滤器(Bloom Filter)这一高效数据结构,结合Go语言实现算法,帮助企业识别和预防泄密风险。通过构建正常操作“指纹库”,实时监测员工操作,快速筛查可疑行为。示例代码展示了如何利用布隆过滤器检测异常操作,并提出优化建议,如调整参数、结合日志分析系统等,全方位筑牢企业信息安全防线,守护核心竞争力。
|
16天前
|
Go语言入门:分支结构
本文介绍了Go语言中的条件语句,包括`if...else`、`if...else if`和`switch`结构,并通过多个练习详细解释了它们的用法。`if...else`用于简单的条件判断;`if...else if`处理多条件分支;`switch`则适用于基于不同值的选择逻辑。特别地,文章还介绍了`fallthrough`关键字,用于优化重复代码。通过实例如判断年龄、奇偶数、公交乘车及成绩等级等,帮助读者更好地理解和应用这些结构。
35 14
|
30天前
|
内网监控系统之 Go 语言布隆过滤器算法深度剖析
在数字化时代,内网监控系统对企业和组织的信息安全至关重要。布隆过滤器(Bloom Filter)作为一种高效的数据结构,能够快速判断元素是否存在于集合中,适用于内网监控中的恶意IP和违规域名筛选。本文介绍其原理、优势及Go语言实现,提升系统性能与响应速度,保障信息安全。
30 5
Go语言中的加密和解密是如何实现的?
Go语言通过标准库中的`crypto`包提供丰富的加密和解密功能,包括对称加密(如AES)、非对称加密(如RSA、ECDSA)及散列函数(如SHA256)。`encoding/base64`包则用于Base64编码与解码。开发者可根据需求选择合适的算法和密钥,使用这些包进行加密操作。示例代码展示了如何使用`crypto/aes`包实现对称加密。加密和解密操作涉及敏感数据处理,需格外注意安全性。
50 14
Go语言中的包(package)是如何组织的?
在Go语言中,包是代码组织和管理的基本单元,用于集合相关函数、类型和变量,便于复用和维护。包通过目录结构、文件命名、初始化函数(`init`)及导出规则来管理命名空间和依赖关系。合理的包组织能提高代码的可读性、可维护性和可复用性,减少耦合度。例如,`stringutils`包提供字符串处理函数,主程序导入使用这些函数,使代码结构清晰易懂。
113 11
Go语言中的map数据结构是如何实现的?
Go 语言中的 `map` 是基于哈希表实现的键值对数据结构,支持快速查找、插入和删除操作。其原理涉及哈希函数、桶(Bucket)、动态扩容和哈希冲突处理等关键机制,平均时间复杂度为 O(1)。为了确保线程安全,Go 提供了 `sync.Map` 类型,通过分段锁实现并发访问的安全性。示例代码展示了如何使用自定义结构体和切片模拟 `map` 功能,以及如何使用 `sync.Map` 进行线程安全的操作。
【01】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-硬件设备实时监控系统运营版发布-本产品基于企业级开源项目Zabbix深度二开-分步骤实现预计10篇合集-自营版
【01】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-硬件设备实时监控系统运营版发布-本产品基于企业级开源项目Zabbix深度二开-分步骤实现预计10篇合集-自营版
25 0
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等