方法一:不考虑并发

  1. import (
  2. "fmt"
  3. "testing"
  4. )
  5. var m *Manager
  6. func GetInstance() *Manager {
  7. if m == nil {
  8. m = &Manager{}
  9. }
  10. return m
  11. }
  12. type Manager struct {
  13. }
  14. func (p Manager) Manage() {
  15. fmt.Println("manage...")
  16. }
  17. func Test01(t *testing.T) {
  18. instance1 := GetInstance()
  19. instance2 := GetInstance()
  20. fmt.Println(instance1==instance2)
  21. }

方法二:考虑并发,但不完善

以上代码虽然实现了单例模式,在非并发的环境下执行,确实没有问题,但是考虑并发的情况,当第一个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...")
}

面试题-medium:手写单例 - 图1