非线程安全的懒汉模式
main.go
package simple
import (
"fmt"
"sync"
"time"
)
var singleton *Singleton
type Singleton struct {
}
func getInstance(wg *sync.WaitGroup) *Singleton {
defer wg.Done()
if nil == singleton {
fmt.Println("init ")
time.Sleep(time.Second)
singleton = &Singleton{}
}
return singleton
}
simple_test.go
package simple
import (
"sync"
"testing"
)
var (
wg sync.WaitGroup
)
func TestSimple(t *testing.T) {
for i := 0; i < 30; i++ {
wg.Add(1)
go getInstance(&wg)
}
wg.Done()
}
线程安全的懒汉模式
使用sync.Mutex 互斥锁实现
main.go
package main
import (
"sync"
)
var wg sync.WaitGroup
func main() {
for i := 0; i < 30; i++ {
wg.Add(1)
go getInstance()
}
wg.Wait()
}
single.go
package main
import (
"fmt"
"sync"
"time"
)
type Singleton struct {
}
var (
mu sync.Mutex
instance *Singleton
)
func getInstance() *Singleton {
defer wg.Done()
if nil == instance {
mu.Lock()
defer mu.Unlock()
if nil == instance {
fmt.Println("init")
time.Sleep(time.Second)
instance = &Singleton{}
}
}
return instance
}
使用sync.Once实现
single.go
package main
import (
"fmt"
"sync"
"time"
)
type single struct{}
var instance *single
var once sync.Once
func getInstance(wg *sync.WaitGroup) *single {
defer wg.Done()
once.Do(func() {
fmt.Println("初始化了")
time.Sleep(time.Second)
instance = &single{}
})
return instance
}
single_test.go
package main
import (
"sync"
"testing"
)
var wg sync.WaitGroup
func TestSingleton(t *testing.T) {
for i := 0; i < 30; i++ {
wg.Add(1)
go getInstance(&wg)
}
wg.Wait()
}
sync.Once
源码
package sync
import (
"sync/atomic"
)
type Once struct {
done uint32
m Mutex
}
func (o *Once) Do(f func()) {
if atomic.LoadUint32(&o.done) == 0 {
o.doSlow(f)
}
}
func (o *Once) doSlow(f func()) {
o.m.Lock()
defer o.m.Unlock()
if o.done == 0 {
defer atomic.StoreUint32(&o.done, 1)
f()
}
}
由于互斥锁的开销较大,sync.Once
利用sync/atomic
包下的原子操作函数检测标志位来降低互斥锁的使用次数来提高性能
饿汉模式
hungry_test.go
package hungry
import (
"fmt"
"testing"
)
func TestHungry(t *testing.T) {
s := GetInstance()
fmt.Println(s)
}
single.go
package hungry
type singleton struct {
}
var instance *singleton = &singleton{}
func GetInstance() *singleton {
return instance
}
比较懒汉模式和饿汉模式
懒汉模式就是在有调用的情况下才会去做初始化,若无调用就不做初始化
饿汉模式一般在程序加载起来的时候就要做初始化,无论是否调用
懒汉模式为了保证不重复创建实例,需要判空,然而所做的判空在并发情况下会同时通过判空条件,重复创建实例。所以说懒汉模式存在线程安全问题
问题就是可能会有多个线程同时进入创建实例的代码部分, 所以程序要做保证创建实例部分代码的原子性