单例
实例的对象只有一个,并提供全局访问的方法。
饿汉式
go:加载go文件所在的包时候,就已经创建好实例了。
java:类加载的时候,就已经创建好了。
- 优点
能够保证,全局唯一性,线程安全。
- 缺点
不支持懒加载(有的时候,我没有真正的时候,但是这个时候就已经初始化了)
- 代码
package singleon
type singleton struct { _ int } // 这里需要指定一个属性,要不然每次创建地址都是同一个
var instance *singleton
// 在该 包 被引用时候,init函数会被调用
func init(){
instance = new(singleton)
}
// 提供一个全局访问的方法
func GetInstance()*Singleton {
return instance
}
懒汉式
- 优点
只有在真正时候的时候才会去,“初始化对象一次”
- 缺点
0、非线程安全,
0、如果创建的对象很复杂,可能还有有延迟。
- 代码
package singleton
type singleton struct { _ int}
var instance *singleton
func GetInstance()*singleton{
// 多个线程/协成操作,可能会创建多个实例,不安全
if instance == nil {
instance = new(singleton)
}
return instance
}
懒汉式+双重检测
- 优点
懒加载,线层安全
- 缺点
0、如果创建的对象很复杂,可能还有有延迟。
0、因为加锁的机制,导致 创建对象产生竞争关系, 单例对象的实例也可能会延迟
package singleton
import "sync"
type singleton struct { _ int}
var instance *singleton
// 使用锁
var lock sync.Mutex
func GetInstance()*singleton{
if instance == nil {
lock.Lock()
defer lock.UnLock()
if instance == nil {
instance = new(singleton)
}else{ // 为什么要在这里 写一个 else,减少 defer 的延迟的时间
return instance
}
}
return instance
}
- 思考为什么 锁 要加在 第二if 的位置呢?
假如你加到 第一个 if 的位置,第一个请求获取到锁了并实例化好了,第二请求也会去获取锁,然后判断,这样做会频繁的获取锁,造成一些不必要的开销。我们只需要在,单例对象实例化好了,优先在判断一下,该对象是否已经实例化了,没有实例化,则 加锁 实例化,否则直接返回。减少 加锁的 粒度,越小越好。
sync.Once实现
package singleton
import "sync"
type singleton struct { _ int}
var instance *singleton
var once sync.Once
func GetInstance()*singleton{
if instance == nil {
once.Do(func(){
instance = new(singleton)
})
}
return instance
}