核心思想
保证一个类永远只能有一个对象、且该对象的功能依然能被其他模块使用
单例模式的逻辑推演
package main
import "fmt"
/*
三个要点:
1.某个类只能有一个实例;
2.它须自行创建这个实例;
3.它须自行向整个系统提供这个实例。
保证一个类永远只能有一个对象
*/
// 1.如果这个类是首字母大写的话,这个类就可以被外界用来创建对象、可能会创建多个对象
// 所以这个类应该是非公有访问、首字母需要小写
type singelton struct{}
// 2.还要有一个指针指向这个唯一对象、但是这个指针永远不能改变方法
// golang中没有常指针概念、因此只能通过将这个指针私有化不让外部模块访问
// 所以指针也是小写的
var instance *singelton = new(singelton)
// 3.如果全部私有化、那么外部模块就无法访问到这个对象
// 需要对外提供一个方法来获取这个对象
func GetInstance() *singelton {
return instance
}
// 4.为对象创建方法
func (s *singelton) Echo() {
fmt.Println("单例的方法")
}
func main() {
s1 := GetInstance()
s1.Echo()
s2 := GetInstance()
if s1 == s2 {
fmt.Println("s1 == s2")
}
}
懒汉式与饿汉式的区别
// 1.懒汉式是在被调用的时候才会去生成对象
func GetInstance1() *singelton1 {
if atomic.LoadUint32(&initialized) == 1 {
return instance1
}
if instance1 == nil {
instance1 = new(singelton1)
}
return instance1
}
// 2.饿汉式是直接生成对象、调用的时候返回
var instance *singelton = new(singelton)
func GetInstance() *singelton {
return instance
}
懒汉式单例与并发安全的三种解决方案
方案1:加互斥锁
package main
import (
"fmt"
"sync"
"sync/atomic"
)
type singelton1 struct{}
var (
lock sync.Mutex
instance1 *singelton1
initialized uint32
)
// 使用互斥锁解决并发问题
func GetInstance1() *singelton1 {
if atomic.LoadUint32(&initialized) == 1 {
return instance1
}
lock.Lock()
defer lock.Unlock()
if instance1 == nil {
instance1 = new(singelton1)
}
return instance1
}
func (s *singelton1) Echo1() {
fmt.Println("懒汉式单例的方法")
}
方案2:使用原子标记
package main
import (
"fmt"
"sync"
"sync/atomic"
)
type singelton1 struct{}
var (
lock sync.Mutex
instance1 *singelton1
initialized uint32
)
// 添加原子操作减少锁竞争
func GetInstance1() *singelton1 {
if atomic.LoadUint32(&initialized) == 1 {
return instance1
}
lock.Lock()
defer lock.Unlock()
if instance1 == nil {
instance1 = new(singelton1)
// 设置原子标记
atomic.StoreUint32(&initialized, 1)
}
return instance1
}
方案3:sync.Once
package main
import (
"fmt"
"sync"
)
type singelton2 struct{}
var (
instance2 *singleton2
once sync.Once
)
func GetInstance2() *singleton2 {
once.Do(func() {
instance2 = &singleton{}
})
return instance2
}
func (s *singelton2) Echo2() {
fmt.Println("懒汉式单例的方法")
}
延申总结
延申1:对于懒汉式单例,高并发情况下可能创建多个对象
需要考虑线程安全,可以加锁
延申2:如果每次获取单例都加锁,性能不好
可以使用原子标记减少锁竞争