Golang 常见设计模式之单例模式
之前我们已经看过了 Golang 常见设计模式中的装饰和选项模式,今天要看的是 Golang 设计模式里最简单的单例模式。单例模式的作用是确保无论对象被实例化多少次,全局都只有一个实例存在。根据这一特性,我们可以将其应用到全局唯一性配置、数据库连接对象、文件访问对象等。Go 语言实现单例模式的方法有很多种,下面我们就一起来看一下。
饿汉式
饿汉式实现单例模式非常简单,直接看代码:
package singleton
type singleton struct{}
var instance = &singleton{}
func GetSingleton() *singleton {
return instance
}
singleton 包在被导入时会自动初始化 instance 实例,使用时通过调用 singleton.GetSingleton() 函数即可获得 singleton 这个结构体的单例对象。
这种方式的单例对象是在包加载时立即被创建,所以这个方式叫作饿汉式。与之对应的另一种实现方式叫作懒汉式,懒汉式模式下实例会在第一次被使用时被创建。
需要注意的是,尽管饿汉式实现单例模式的方式简单,但大多数情况下并不推荐。因为如果单例实例化时初始化内容过多,会造成程序加载用时较长。
懒汉式
接下来我们再来看下如何通过懒汉式实现单例模式:
package singleton
type singleton struct{}
var instance *singleton
func GetSingleton() *singleton {
if instance == nil {
instance = &singleton{}
}
return instance
}
相较于饿汉式的实现,懒汉式将实例化 singleton 结构体部分的代码移到了 GetSingleton() 函数内部。这样能够将对象实例化的步骤延迟到 GetSingleton() 第一次被调用时。
不过通过 instance == nil 的判断来实现单例并不十分可靠,如果有多个 goroutine 同时调用 GetSingleton() 就无法保证并发安全。
支持并发的单例
如果你使用 Go 语言写过并发编程,应该很快能想到该如何解决懒汉式单例模式并发安全问题,比如像下面这样:
package singleton
import "sync"
type singleton struct{}
var instance *singleton
var mu sync.Mutex
func GetSingleton() *singleton {
mu.Lock()
defer mu.Unlock()
if instance == nil {
instance = &singleton{}
}
return instance
}
上面代码的修改是通过加锁机制,即在 GetSingleton() 函数最开始加了如下两行代码:
mu.Lock()
defer mu.Unlock()
加锁的机制可以有效保证这个实现单例模式的函数是并发安全的。
不过使用了锁机制也带来了一些问题,这让每次调用 GetSingleton() 时程序都会进行加锁、解锁的步骤,从而导致程序性能的下降。
双重锁定
加锁会导致程序性能下降,但又不用锁又无法保证程序的并发安全。为了解决这个问题有人提出了双重锁定(Double-Check Locking)的方案:
package singleton
import "sync"
type singleton struct{}
var instance *singleton
var mu sync.Mutex
func GetSingleton() *singleton {
if instance == nil {
mu.Lock()
defer mu.Unlock()
if instance == nil {
instance = &singleton{}
}
}
return instance
}
通过上面的可以看到,所谓双重锁定实际上就是在程序加锁前又加了一层 instance == nil 判断,通过这种方式来兼顾性能和安全两个方面。不过这让代码看起来有些奇怪,外层已经判断了 instance == nil,但是加锁后又进行了第二次 instance == nil 判断。
其实外层的 instance == nil 判断是为了提高程序的执行效率,免去原来每次调用 GetSingleton() 都上锁的操作,将加锁的粒度更加精细化。简单说就是如果 instance 已经存在,则无需进入 if 逻辑,程序直接返回 instance 即可。而内层的 instance == nil 判断则考虑了并发安全,考虑到万一在极端情况下,多个 goroutine 同时走到了加锁这一步,内层判断会在这里起到作用。
Gopher 惯用方案
虽然双重锁定机制兼顾和性能和并发安全,但显然代码有些丑陋,不符合广大 Gopher 的期待。好在 Go 语言在 sync 包中提供了 Once 机制能够帮助我们写出更加优雅的代码:
package singleton
import "sync"
type singleton struct{}
var instance *singleton
var once sync.Once
func GetSingleton() *singleton {
once.Do(func() {
instance = &singleton{}
})
return instance
}
Once 是一个结构体,在执行 Do 方法的内部通过 atomic 操作和加锁机制来保证并发安全,且 once.Do 能够保证多个 goroutine 同时执行时 &singleton{} 只被创建一次。
其实 Once 并不神秘,其内部实现跟上面使用的双重锁定机制非常类似,只不过把 instance == nil 换成了 atomic 操作,感兴趣的同学可以查看下其对应源码。
总结
以上就是 Go 语言中实现单例模式的几种常用套路,经过对比可以得出结论,最推荐的方式是使用 once.Do 来实现,sync.Once 包帮我们隐藏了部分细节,却可以让代码可读性得到很大提升。
推荐阅读
Golang 常见设计模式之单例模式的更多相关文章
- Golang 常见设计模式之选项模式
熟悉 Python 开发的同学都知道,Python 有默认参数的存在,使得我们在实例化一个对象的时候,可以根据需要来选择性的覆盖某些默认参数,以此来决定如何实例化对象.当一个对象有多个默认参数时,这个 ...
- Java常见设计模式之单例模式
1.何为单例模式? 单例模式是一种常用的软件设计模式.在它的核心结构中只包含一个被称为单例类的特殊类.通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的 ...
- Golang 常见设计模式之装饰模式
想必只要是熟悉 Python 的同学对装饰模式一定不会陌生,这类 Python 从语法上原生支持的装饰器,大大提高了装饰模式在 Python 中的应用.尽管 Go 语言中装饰模式没有 Python 中 ...
- java设计模式之单例模式(几种写法及比较)
概念: Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记式单例. 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建 ...
- 每天一个设计模式-4 单例模式(Singleton)
每天一个设计模式-4 单例模式(Singleton) 1.实际生活的例子 有一天,你的自行车的某个螺丝钉松了,修车铺离你家比较远,而附近的五金店有卖扳手:因此,你决定去五金店买一个扳手,自己把螺丝钉固 ...
- [转]JAVA设计模式之单例模式
原文地址:http://blog.csdn.net/jason0539/article/details/23297037 概念: java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主 ...
- Java设计模式03:常用设计模式之单例模式(创建型模式)
1. Java之单例模式(Singleton Pattern ) 单例模式是一种常见的设计模式,单例模式分三种:懒汉式单例.饿汉式单例.登记式单例三种. 单例模式有一下特点: 1.单例类只能有一个实 ...
- java设计模式之单例模式(七种方法)
单例模式:个人认为这个是最简单的一种设计模式,而且也是在我们开发中最常用的一个设计模式. 单例模式的意思就是只有一个实例.单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例.这个 ...
- JavaScript 中常见设计模式整理
开发中,我们或多或少地接触了设计模式,但是很多时候不知道自己使用了哪种设计模式或者说该使用何种设计模式.本文意在梳理常见设计模式的特点,从而对它们有比较清晰的认知. JavaScript 中常见设计模 ...
随机推荐
- python爬虫---爬取网易云音乐
代码: import requests from lxml import etree text = requests.get("https://music.163.com/discover/ ...
- Shiro 安全框架详解一(概念+登录案例实现)
shiro 安全框架详细教程 总结内容 一.RBAC 的概念 二.两种常用的权限管理框架 1. Apache Shiro 2. Spring Security 3. Shiro 和 Spring Se ...
- windows下的volatility取证分析与讲解
volatility(win64) 1.下载 volatility 下载地址:(我下载的版本2.6,并把名字稍微改了一下) Release Downloads | Volatility Foundat ...
- XShell免费版的安装配置教程以及使用教程(超级详细)
一. XShell的作用 XShell可以在Windows界面下来访问远端不同系统下的服务器,从而比较好的达到远程控制终端的目的.它支持 RLOGIN.SFTP.SERIAL.TELNET.SSH2 ...
- 记录Jenkins升级到最新版遇到的问题
首先吐槽一下Jenkins: 1.安装插件的时候无法根据Jenkins的版本号安装对应的插件! 2.安装插件安装版本不一致的时候无法降低插件版本! 3.为啥要我们升级到最新版! 一.升级原因 我的 ...
- zabbix自定义自动发现模板
需求: 自定义发现磁盘io,并实现监控.其他的业务组件自动发现监控其实也和这个大同小异,自动发现主要逻辑就是你要根据组件规则自动匹配出需要监控的所有组件,再通过传参的方式获取对应组件数据. 自动发现无 ...
- 在 Docker 上快速运行 Apache Airflow 2.2.4
Docker 安装 Apache Airflow 参考资料 Running Airflow in Docker 安装依赖 Docker Engine Docker Composite 快速运行 Apa ...
- MongoDB 提供的工具
MongoDB 提供的工具 官网 https://www.mongodb.com/ 工具 MongoDB 提供了一些工具,: MongoDB Compass: MongoDB数据库可视化工具(类似PL ...
- HCIE-SEC笔记-第四节-网络入侵和防火墙基础
等级保护: 网络安全:防火墙.VPN.准入控制 渗透测试: 防火墙:区域隔离和访问控制 数字与研究公司:用数据说话 IDC:国际数据公司 Gartner:著名的数字与咨询公司 弗雷斯特: 数世咨询: ...
- MATLAB R2019b超详细安装教程(附完整安装文件)
摘要:本文详细介绍Matlab的安装步骤,为方便安装这里提供了完整安装文件的百度网盘下载链接供大家使用.从文件下载到证书安装本文都给出了每个步骤的截图,按照图示进行即可轻松完成安装使用.本文目录如首页 ...