单例模式-Golang实现

非线程安全的懒汉模式

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()
}

image-20220105202604177

线程安全的懒汉模式

使用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
}

比较懒汉模式和饿汉模式

懒汉模式就是在有调用的情况下才会去做初始化,若无调用就不做初始化

饿汉模式一般在程序加载起来的时候就要做初始化,无论是否调用

懒汉模式为了保证不重复创建实例,需要判空,然而所做的判空在并发情况下会同时通过判空条件,重复创建实例。所以说懒汉模式存在线程安全问题

问题就是可能会有多个线程同时进入创建实例的代码部分, 所以程序要做保证创建实例部分代码的原子性

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值