设计模式-单例模式
概念
单例模式 是一种常见的设计模式,用于确保一个类只有一个实例,并提供一个全局访问点。
根据初始化的时机不同,单例模式又分为 懒汉式
和 饿汉式
下面通过多个实例
懒汉式在第一次使用时创建实例对象,
饿汉式在程序启动时就创建实例对象。
饿汉式
type singleton struct {}
var instance *singleton
func init() {
instance = new(singleton)
}
func GetInstance() *singleton { return instance }
饿汉式
是 线程安全 的,因为在调用GetInstance方法之前 instance已经被实例化好
懒汉式
下面再看下 懒汉式 单例模式,
通过几个版本,带大家彻底理解单例模式
# Version 1
type singleton struct{}
var instance *singleton
func GetInstance() *singleton {
if instance == nil {
instance = new(singleton)
}
return instance
}
存在的问题:多线程环境下可能同时进入 if instance == nil
的判断条件,从而创建出多个实例
# Version2
type singleton struct{}
var (
mu sync.Mutex
instance *singleton
)
func GetInstance() *singleton {
mu.Lock()
defer mu.Unlock()
if instance == nil {
instance = new(singleton)
}
return instance
}
存在问题:加锁机制降低系统并发,造成CPU浪费
# Version 3
type singleton struct{}
var (
mu sync.Mutex
instance *singleton
)
func GetInstance() *singleton {
if instance == nil {
mu.Lock()
defer mu.Unlock()
if instance == nil {
instance = new(singleton)
}
}
return instance
}
该版本解决了
- 并发调用产生多个实例的问题
- 版本2,上来就加锁的问题
加锁的目的 就是为了 实例化 的动作由 当个线程来完成,该功能Go内部有现成封装好的供我们使用
# Version 4
// 注意:这里是小写,不可导出
type singleton struct {}
var (
instance *singleton
once sync.Once
)
func GetInstance() *singleton {
once.Do(func() {
instance = &singleton{}
})
return instance
}
在上面的示例中,我们定义了一个名为singleton
的结构体类型,该结构体表示单例对象。然后,我们声明一个名为instance
的变量来保存单例实例,并使用sync.Once
类型的变量once
来确保初始化过程只执行一次。
GetInstance
函数是一个全局访问点,用于获取单例实例。在该函数内部,我们使用once.Do
函数来确保实例只被创建一次。once.Do
接受一个函数作为参数,该函数将在第一次调用once.Do
时执行。在这个函数中,我们创建了一个新的singleton
实例并将其赋值给instance
变量。
这样,无论在代码中的哪个位置调用GetInstance
函数,都将返回同一个单例实例。
使用单例模式的好处是可以避免创建多个相同的对象,节省了内存和系统资源。同时,它还提供了一个统一的访问点,便于对单例对象进行操作。
需要注意的是,上述实现在多线程环境中是线程安全的。sync.Once
的机制能够确保并发访问时只有一个goroutine执行初始化操作。
模式应用场景
单例模式在以下几种场景中是常用的:
资源共享:当系统中有多个模块或对象需要共享同一个资源时,可以使用单例模式来管理该资源,确保只有一个实例被创建和访问。例如,数据库连接池、线程池等资源的管理。
配置信息:单例模式可用于管理全局的配置信息,保证系统中的各个模块都使用同一份配置数据。这样可以避免不同模块之间的配置冲突和数据不一致。
日志记录器:在日志系统中,通常希望只有一个日志记录器实例来负责日志的写入,以避免并发写入导致的问题。单例模式可以确保只有一个日志记录器实例被创建和使用。
缓存:单例模式可以用于实现缓存系统,确保缓存的一致性和有效性。通过使用单例模式,多个模块可以共享同一个缓存实例,避免重复创建缓存对象。
GUI应用程序中的窗口管理器:在GUI应用程序中,窗口管理器负责管理和控制所有窗口的创建和关闭。使用单例模式可以确保只有一个窗口管理器实例存在,从而保持窗口管理的一致性。
需要注意的是,单例模式的使用应该谨慎,过度使用单例可能导致代码的可测试性
和可扩展性
降低。在设计中,应该根据具体场景和需求来决定是否使用单例模式。