单例模式:是一种常见的设计模式,用于确保一个类只有一个实例,并提供全局访问点。在实际应用中,有些对象只需要一个实例,比如线程池、数据库连接池等。使用单例模式可以确保只有一个实例存在,避免了资源的浪费和冲突。
Golang中实现单例模式的两种形式:饿汉式和懒汉式。
饿汉式单例模式
饿汉式单例模式是指在程序启动时就创建单例对象,以保证在程序运行期间只有一个实例。
go
package single_hungry
import "fmt"
// 饿汉式单例模式
var s *singleInstance
type singleInstance struct {
}
func init() {
s = newSingleInstance()
}
func newSingleInstance() *singleInstance {
return &singleInstance{}
}
func (s *singleInstance) Work() {
fmt.Println("single_hungry work function....")
}
type Instance interface {
Work()
}
func GetSingleInstance() Instance {
return s
}
go
package main
import (
"singleton/single_hungry"
)
func getSingleHungry() single_hungry.Instance {
return single_hungry.GetSingleInstance()
}
func main() {
s := getSingleHungry()
s.Work()
}
懒汉式单例模式
懒汉式单例模式是指在需要使用单例对象时才创建实例,以避免在程序启动时就创建对象,浪费资源。
懒汉式存在并发问题,因此需要通过互斥锁的保护,保证初始化工作一定只会执行一次。
但也存在同时两个goroutine抢锁,gotoutine1抢到锁进行初始化,goroutine2等待锁释放,gotoutine1执行完初始化工作后释放锁,gotoutine2获得锁继续往下执行,这样情况会导致初始化工作不止执行一次。因此需要通过double check的思想,在加锁成功后进行第二次检查是否被初始化过
go
package single_lazy
import (
"fmt"
"sync"
)
// 懒汉式单例模式
var (
s *singleInstance
mutex sync.Mutex
)
type singleInstance struct {
}
func newSingleInstance() *singleInstance {
return &singleInstance{}
}
func (s *singleInstance) Work() {
fmt.Println("single_lazy work function....")
}
type Instance interface {
Work()
}
func GetSingleInstance() Instance {
if s != nil {
fmt.Println("single_lazy already created....")
return s
}
mutex.Lock()
defer mutex.Unlock()
if s != nil {
fmt.Println("single_lazy already created.....")
return s
}
fmt.Println("Creating single_lazy now.....")
s = newSingleInstance()
return s
}
go
package main
import (
"singleton/single_lazy"
"time"
)
func getSingleLazy() single_lazy.Instance {
return single_lazy.GetSingleInstance()
}
func main() {
for i := 0; i < 30; i++ {
go getSingleLazy()
}
time.Sleep(time.Second * 3)
}
通过sync.Onces实现懒汉式单例模式
go
package single_once
import (
"fmt"
"sync"
)
// 懒汉式单例模式
var (
s *singleInstance
once sync.Once
)
type singleInstance struct {
}
func newSingleInstance() *singleInstance {
return &singleInstance{}
}
func (s *singleInstance) Work() {
fmt.Println("single_once work function....")
}
type Instance interface {
Work()
}
func GetSingleInstance() Instance {
once.Do(func() {
s = newSingleInstance()
})
return s
}
总结
饿汉式在程序加载时就创建好,不存在线程安全问题,没有加锁操作,不会出现同步控制的性能损耗;但在程序加载时就创建好实例,如果实例一直未被使用,会造成资源浪费,无法做到按需创建对象;适用于对性能要求较高的场景
懒汉式只在需要时创建对象,节省了内存资源,通过加锁保证了多线程环境下的同步访问;由于需要加锁进行同步控制,会造成一定的性能损耗。锁粒度较大,对整个方法加锁,即使只有第一次创建对象时需要同步,后续的访问也需要等待,适用于对性能要求不是很高的场景。