创建型:单例模式
单例模式
单例模式是创建全局唯一的对象所使用的设计模式。比如,存储 log,记录时间,更新缓存的对象等等。
实现
饿汉单例
单例模式的实现,我们很自然的想到在 init
中创建私有全局变量对象,然后使用 GetObject
获取对象的方式来实现。比如:
package logger
type logger struct{}
var l *logger = nil
func GetLogger() *logger {
return l
}
func init() {
l = &logger{}
}
这是启动时创建单例的方式,称作饿汉单例。
因为是在启动时创建,如果单例并不需要,或者有选择性的需要,这种启动时创建就会造成一种资源的浪费,即创建了不被使用的对象。第二,如果创建对象时间较长或者使用资源较多等等会造成应用启动慢。
能否在有需要的时候创建单例对象呢?这就是所谓的懒汉单例。
懒汉单例
懒汉单例实现如下。
package logger
// private logger object
type logger struct{}
// unique object
var l *logger = nil
// Get unique object from GetLogger
func GetLogger() *logger {
return l
}
// Create unique object
func NewLogger() *logger {
if l == nil {
l = &logger{}
}
return l
}
注意,这里的实例可以保证全局唯一,但是对于多线程单例创建无法保证全局唯一。因此,我们要给对象加锁,确保只有一个线程进入临界区创建单例。
func NewLogger() *logger {
var m sync.Mutex
m.Lock()
defer m.Unlock()
if l == nil {
l = &logger{}
}
return l
}
这么实现也要问题,会导致多线程创建单例时线程处于等待锁状态,我们可以在请求锁加上单例对象判断,来保证如果当前线程获取到全局唯一对象则返回,跳过等待锁阶段。
func NewLogger() *logger {
if l == nil {
var m sync.Mutex
m.Lock()
defer m.Unlock()
if l == nil {
l = &logger{}
}
}
return l
}
思考
- 单例创建的两种方式中,要结合具体场景看使用哪一种。使用懒汉方式创建单例,如果创建时间较长,且该单例启动就需要的话则可以考虑饿汉单例实现,不要把创建时间较长放到使用阶段。
- 多进程的单例,有点类似于分布式系统的集群唯一。可以使用访问全局唯一资源(类似于锁实现)来确保集群唯一。
- Go 使用
sync.Once
创建单例。在 Go 中sync.Once
不局限于创建单例,也包括创建全局唯一的行为。如果这个行为只应执行一次的话,那么可以使用sync.Once
。
芝兰生于空谷,不以无人而不芳。
分类:
设计模式
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现