方法一:不考虑并发
import (
"fmt"
"testing"
)
var m *Manager
func GetInstance() *Manager {
if m == nil {
m = &Manager{}
}
return m
}
type Manager struct {
}
func (p Manager) Manage() {
fmt.Println("manage...")
}
func Test01(t *testing.T) {
instance1 := GetInstance()
instance2 := GetInstance()
fmt.Println(instance1==instance2)
}
方法二:考虑并发,但不完善
以上代码虽然实现了单例模式,在非并发的环境下执行,确实没有问题,但是考虑并发的情况,当第一个goroutine执行到m=&Manager{}之前,第二个gotoutine也来获取实例了,第二个gotoutine去判断m是不是等于nil,因为m=&Manager{}还没来得及执行,所以m=nil,那么if中的m = &Manager{}就可能会被执行两遍。
利用go的锁机制sync.Mutex修改以上代码。
import (
"fmt"
"sync"
"testing"
)
var m *Manager
var lock *sync.Mutex = &sync.Mutex{}
func GetInstance() *Manager {
lock.Lock()
defer lock.Unlock()
if m == nil {
m = &Manager{}
}
return m
}
type Manager struct {
}
func (p Manager) Manage() {
fmt.Println("manage...")
}
func Test01(t *testing.T) {
instance1 := GetInstance()
instance2 := GetInstance()
fmt.Println(instance1==instance2)
}
方法三:并发优化
使用如下代码修改之后,保证只有一个goroutine能够执行GetInstance函数,这样并发的问题就解决了,但是现在又有一个问题,每次goroutine来的时候都会被锁挡在GetInstance之外等上一个goroutine执行结束,这样代码执行效率肯定会下降,下面我们引入双重锁机制类修改我们的代码。
import (
"fmt"
"sync"
)
var m *Manager
var lock *sync.Mutex = &sync.Mutex{}
func GetInstance() *Manager {
if m == nil {
lock.Lock()
defer lock.Unlock()
if m == nil {
m = &Manager{}
}
}
return m
}
type Manager struct {
}
func (p Manager) Manage() {
fmt.Println("manage...")
}
方法四:sync.Once优化写法
使用Golang中的sync.Once可以实现比较优雅的写法
import (
"fmt"
"sync"
)
var m *Manager
var once sync.Once
func GetInstance() *Manager {
once.Do(func() {
m = &Manager{}
})
return m
}
type Manager struct {
}
func (p Manager) Manage() {
fmt.Println("manage...")
}