golang设计模式之单例模式
核心作用
保证一个类只有一个实例,并且提供一个访问该实例的全局访问点
优点
- 由于单例模式只生成一个实例,减少系统性能开销,当一个对象的生产需要比较多资源时,就可以通过在应用启动的时候直接产生一个单例对象,然后永久驻留内存,例如日志模块
- 单例模式可以在系统设置全局访问点,优化资源共享问题,比如数据库里连接池
常见几种实现方式
- 饿汉式(调用效率高,不能延时加载)
- 懒汉式(调用效率不高,可以延时加载)
- 懒汉加锁(调用效率不高,可以延时加载)
- 双重锁(调用效率不高,可以延时加载)
- once单例(调用效率高,不能延时加载)
简单实现
1. 饿汉
缺点:系统初始化时加载,有可能没有用到GetInstance方法,但是一直是占用资源的,如果初始化比较复杂,加载时间会长
type SingletonDemon2 struct {}
var singletonDemon2 = &SingletonDemon2{}
func GetInstance() *SingletonDemon2{
return singletonDemon2
}
2. 懒汉
缺点:延时加载,懒加载,真正用的时候才加载,资源利用率高了,但是每次调GetInstance用都要同步,并发量高得时候有可能会建立多个对象
type SingletonDemon1 struct {}
var singletonDemon1 *SingletonDemon1
func GetInstance() *SingletonDemon1 {
if singletonDemon1 == nil {
singletonDemon1 = &SingletonDemon1{}
}
return singletonDemon1
}
3. 懒汉加锁模式
缺点:多线程并发量高的时候,每次都要lock,影响性能
type SingleTonDemon3 struct {}
var singletonDemon3 *SingleTonDemon3
var mutex sync.Mutex
func GetInstance3() *SingleTonDemon3{
mutex.Lock()
defer mutex.Unlock()
if singletonDemon3 == nil{
singletonDemon3 = &SingleTonDemon3{}
}
return singletonDemon3
}
4. 双重锁模式
判断不为空时在加锁,当instance为null并且同时有2个线程调用getInstance方法时候,他们都会通过第一重的instance == null 的判断。如果没有第二个判断,就多new了空间,而且不能释放。
type SingleTonDemon4 struct {}
var singletonDemon4 *SingleTonDemon4
var mutx sync.Mutex
func GetInstance4() *SingleTonDemon4{
if singletonDemon4 == nil{
mutx.Lock()
if singletonDemon4 == nil{
singletonDemon4 = &SingleTonDemon4{}
mutx.Unlock()
return singletonDemon4
}
}
mutx.Unlock()
return singletonDemon4
}
5. sync.once
看once.Do源码可以看到只有在 done 等于 0 的时候才调用 f(),一旦调用后 done 的值被置为 1
type Singleton struct {}
var singleton *Singleton
var once sync.Once
func GetInstance() *Singleton {
once.Do(func() {
singleton = &Singleton{}
})
return singleton
}