目录导航
- 1.单例模式
- 1)什么是单例模式
- 2)使用场景
- 3)实现方式
- 1.懒汉式单例模式
- 2.饿汉式
- 3.双重检查式
- 4.`sysc.Once`式
- 4)实践案例
- 5)优缺点分析
1.单例模式
1)什么是单例模式
单例模式(Singleton Pattern)是一种常用的设计模式。单例模式的类提供了一种访问其唯一对象的方法,该对象可以直接访问,无须实例化。单例模式保证了一个类的对象只存在一个,同时维护一个对其对象的全局访问点。
uml类图:
2)使用场景
- 如果开发者想要更加严格地控制全局变量,则可以使用单例模式。单例模式可以保证一个类只存在一个实例。除了单例模式外,无法通过其他方式替换缓存的实例
- 如果程序中的某个类对于所有客户端都只有一个可用的实例,则可以使用单例模式。在单例模式中,如果对象已经被创建,则返回已有的对象
- 在设计数据库连接池时,可以使用单例模式。在数据库管理系统中使用数据库连接池,主要目的是节省打开或关闭数据库连接引起的效率损耗。这种效率上的损耗还是非常巨大的,如果使用单例模式来进行维护的话,则可以大大减少这中类似的损耗。
3)实现方式
在golang中,单例模式的实现方式有4中,分别是懒汉式、饿汉式、双重检查式、和sysnc.Once。而这四种单例模式都有一个共同的特征:只实例化一次,只允许一个实例存在。
1.懒汉式单例模式
懒汉式单例模式指的是在创建对象时,不直接创建对象,在加载配置文件时才创建对象。
package typesofSigton
import "sync"
type singleton struct{
value int
}
//声明私有指令
var instance *singleton
//加锁的目的是保证协成并发安全
//mo1.声明锁对象
var mutex sync.Mutex
//获取单例对象
func GetInstance() *singleton {
//加锁,保证协成安全
mutex.Lock()
defer mutex.Unlock()
if instance==nil{
instance=new (singleton)
}
return instance
}
以上代码通过加锁的方式保证了协成的并发安全但是代码有一个问题就是每次调用该方法都需要进行加锁的操作,在性能上则不够高效。
2.饿汉式
饿汉式是指,在创建对象时,不判断创建的对象是否为空,直接创建对象。饿汉式是并发安全的,其唯一的缺点是在导入包的同时会创建对象,并且创建的对象会持续存储于内存当中。饿汉式单例模式可以使用初始化init()函数实现
package typesofSigton
import "fmt"
//var instance *singleton
func init(){
if instance==nil{
instance=new(singleton)
fmt.Println("创建单个实例")
}
}
//编写提供实力的函数
func GetInstance() *singleton {
return instance
}
3.双重检查式
在懒汉式的基础上进行优化,减少加锁的操作,这样可以在保证并发安全的同时不影响性能,这种模式也被称为双重检查单例模式
package typesofSigton
import (
"fmt"
"sync"
)
//声明锁对象
var mutex sync.Mutex
//当对象为空时,对对象进行加锁操作;在创建好对象后,在获取对象时就不用加锁了
func GetIntance() *singleton{
if instance ==nil{
mutex.Lock()
if instance==nil{
instance=new(singleton)
fmt.Println("创建单个实例")
}
mutex.Unlock()
}
return instance
}
4.sysc.Once
式
sysc.Once
是go标准库提供的使函数只执行一次的实现,通常应用于单例模式,如初始化配置、保持数据库连接等。其作用与init()
函数类似,但有区别。init()
函数会在其所在的包首次被加载时执行,如果被加载的包不立即被使用,那么既浪费了内存空间,又延长了程序加载时间。
sysc.Once
可以在代码的任意位置被初始化和调用,在并发场景中是并发安全的,使用sysc.Once
对象的Do()方法创建实例,可以确保创建对象的方法只被执行一次
package typesofSigton
import (
"fmt"
"sync"
)
var once sync.Once
func GetIntance() *singleton{
once.Do(func() {
instance=new(singleton)
fmt.Println("创建单个实例")
})
return instance
}
4)实践案例
excample.go
package excample
import (
"fmt"
"sync"
)
var lock = &sync.Mutex{}
type singleton struct {
}
var instance *singleton
// 获取实例对象
func GetInstance() *singleton {
if instance == nil {
lock.Lock()
defer lock.Unlock()
if instance == nil {
fmt.Println("创建单个实例")
instance = new(singleton)
} else {
fmt.Println("已创建单个实例")
}
} else {
fmt.Println("已创建单个实例")
}
return instance
}
main.go
package main
import (
"Signton/excample"
"fmt"
)
func main() {
for i := 0; i < 3; i++ {
go excample.GetInstance()
}
fmt.Scanln()
}
//运行结果为
//创建单个实例
//已创建单个实例
//已创建单个实例
5)优缺点分析
优点:
- 单例模式可以扩展为工厂模式。
- 由于系统的内存中只存在一个对象,因此对于需要频繁创建和销毁对象的系统,使用单例模式可以提升系统的性能
缺点:
- 由于单例模式不是抽象的,因此其扩展性较低
- 滥用单例模式会产生一些负面问题。例如,为了节省资源,如果使用单例模式设计数据连接池对象,则可能会导致共享连接池对象过多且没有被释放的场景,从而出现连接池溢出的问题。此外,如果实例化的对象长时间不被使用,那么他可能会被操作系统认为是垃圾对象而回收,从而导致对象缺失。