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

简介: 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
相关实践学习
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
目录
相关文章
|
设计模式 缓存 安全
【设计模式】【创建型模式】单例模式(Singleton)
一、入门 什么是单例模式? 单例模式是一种设计模式,确保一个类只有一个实例,并提供一个全局访问点。它常用于需要全局唯一对象的场景,如配置管理、连接池等。 为什么要单例模式? 节省资源 场景:某些对象创
476 15
|
设计模式 安全 Java
设计模式:单例模式
单例模式是一种创建型设计模式,确保一个类只有一个实例,并提供全局访问点。它通过私有化构造函数、自行创建实例和静态方法(如`getInstance()`)实现。适用于数据库连接池、日志管理器等需要全局唯一对象的场景。常见的实现方式包括饿汉式、懒汉式、双重检查锁、静态内部类和枚举。线程安全问题可通过`synchronized`或双重检查锁解决,同时需防止反射和序列化破坏单例。优点是避免资源浪费,缺点是可能增加代码耦合度和测试难度。实际开发中应优先选择枚举或静态内部类,避免滥用单例,并结合依赖注入框架优化使用。
|
设计模式 存储 安全
设计模式2:单例模式
单例模式是一种创建型模式,确保一个类只有一个实例,并提供全局访问点。分为懒汉式和饿汉式: - **懒汉式**:延迟加载,首次调用时创建实例,线程安全通过双重检查锁(double check locking)实现,使用`volatile`防止指令重排序。 - **饿汉式**:类加载时即创建实例,线程安全但可能浪费内存。 示例代码展示了如何使用Java实现这两种模式。
344 4
|
设计模式 存储 安全
设计模式-单例模式练习
单例模式是Java设计模式中的重要概念,确保一个类只有一个实例并提供全局访问点。本文详解单例模式的核心思想、实现方式及线程安全问题,包括基础实现(双重检查锁)、懒汉式与饿汉式对比,以及枚举实现的优势。通过代码示例和类图,深入探讨不同场景下的单例应用,如线程安全、防止反射攻击和序列化破坏等,展示枚举实现的简洁与可靠性。
245 0
|
设计模式 存储 前端开发
前端必须掌握的设计模式——单例模式
单例模式是一种简单的创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点。适用于窗口对象、登录弹窗等场景,优点包括易于维护、访问和低消耗,但也有安全隐患、可能形成巨石对象及扩展性差等缺点。文中展示了JavaScript和TypeScript的实现方法。
712 13
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
251 2
|
设计模式 安全 Java
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
Kotlin教程笔记(57) - 改良设计模式 - 单例模式
|
设计模式 Java 数据库连接
【设计模式】【创建型模式】工厂方法模式(Factory Methods)
一、入门 什么是工厂方法模式? 工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它定义了一个用于创建对象的接口,但由子类决定实例化哪个类。工厂方法模式使类的实例化延迟
386 16
|
设计模式 负载均衡 监控
并发设计模式实战系列(2):领导者/追随者模式
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发设计模式实战系列,第二章领导者/追随者(Leader/Followers)模式,废话不多说直接开始~
382 0
|
设计模式 监控 Java
并发设计模式实战系列(1):半同步/半异步模式
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发设计模式实战系列,第一章半同步/半异步(Half-Sync/Half-Async)模式,废话不多说直接开始~
552 0

热门文章

最新文章