关于golang:go解锁设计模式之单例模式有个小坑要注意一下啊

35次阅读

共计 3435 个字符,预计需要花费 9 分钟才能阅读完成。

前言

哈喽,大家好,我是 asong,这是我的第 16 篇原创文章,感激各位的关注。明天给大家分享设计模式之单例模式,并应用 go 语言实现。相熟 java 的同学对单例模式肯定不生疏,单例模式,是一种很常见的软件设计模式,在他的外围构造中只蕴含一个被称为单例的非凡类。通过单例模式能够保证系统中一个类只有一个实例且该实例易于外界拜访,从而不便对实例个数的管制并节约系统资源。上面咱们就一起来看一看怎么应用 go 实现单例模式,这里有一个小坑,肯定要留神一下,结尾通知你哦~~~

什么是单例模式

单例模式确保某一个类只有一个实例。为什么要确保一个类只有一个实例?有什么时候才须要用到单例模式呢?听起来一个类只有一个实例如同没什么用呢!那咱们来举个例子。比方咱们的 APP 中有一个类用来保留运行时全局的一些状态信息,如果这个类实现不是单例的,那么 App 外面的组件可能随便的生成多个类用来保留本人的状态,等于大家各玩各的,那这个全局的状态信息就成了笑话了。而如果把这个类实现成单例的,那么不论 App 的哪个组件获取到的都是同一个对象(比方 Application 类,除了多过程的状况下)。

饿汉模式

这里咱们应用三种形式实现饿汉模式。先说一下什么是懒汉模式吧,从懒汉这两个字,咱们就能晓得,这个人很懒,所以他不可能在未应用实例时就创立了对象,他必定会在应用时才会创立实例,这个益处的就在于,只有在应用的时候才会创立该实例。上面咱们一起来看看他的实现:

  • 不加锁
package one

type singleton struct {

}

var  instance *singleton
func GetInstance() *singleton {
    if instance == nil{instance = new(singleton)
    }
    return instance
}

这种办法是会存在线程平安问题的,在高并发的时候会有多个线程同时掉这个办法,那么都会检测 instance 为 nil,这样就会导致创立多个对象,所以这种办法是不举荐的,我再来看第二种写法。

  • 整个办法加锁
type singleton struct {

}

var instance *singleton
var lock sync.Mutex

func GetInstance() *singleton {lock.Lock()
    defer lock.Unlock()
    if instance == nil{instance = new(singleton)
    }
    return instance
}

这里对整个办法进行了加锁,这种能够解决并发平安的问题,然而效率就会降下来,每一个对象创立时都是进行加锁解锁,这样就拖慢了速度,所以不举荐这种写法。

  • 创立办法时进行锁定
type singleton struct {

}

var instance *singleton
var lock sync.Mutex

func GetInstance() *singleton {
    if instance == nil{lock.Lock()
        instance = new(singleton)
        lock.Unlock()}
    return instance
}

这种办法也是线程不平安的,尽管咱们加了锁,多个线程同样会导致创立多个实例,所以这种形式也不是举荐的。所以就有了上面的双重检索机制

  • 双重检锁
type singleton struct { }

var instance *singleton
var lock sync.Mutex

func GetInstance() *singleton {
    if instance == nil{lock.Lock()
        if instance == nil{instance = new(singleton)
        }
        lock.Unlock()}
    return instance
}

这里在下面的代码做了改良,只有当对象未初始化的时候,才会有加锁和减锁的操作。然而又呈现了另一个问题:每一次拜访都要查看两次,为了解决这个问题,咱们能够应用 golang 规范包中的办法进行原子性操作。

  • 原子操作实现
type singleton struct { }

var instance *singleton
var once sync.Once
func GetInstance() *singleton {once.Do(func() {instance = new(singleton)
    })
    return instance
}

这里应用了 sync.OnceDo办法能够实现在程序运行过程中只运行一次其中的回调,这样就能够只创立了一个对象,这种办法是举荐的~~~。

饿汉模式

有懒汉模式,当然还要有饿汉模式啦,看了懒汉的模式,饿汉模式咱们很好解释了,因为他饿呀,所以很着急的就创立了实例,不必等到应用时才创立,这样咱们每次调用获取接口将不会从新创立新的对象,而是间接返回之前创立的对象。比拟实用于:如果某个单例应用的次数少,并且创立单例音讯的资源比拟多,那么就须要实现单例的按需创立,这个时候懒汉模式就是一个不错的抉择。不过也有毛病,饿汉模式将在包加载的时候就会创立单例对象,当程序中用不到该对象时,节约了一部分空间,然而绝对于懒汉模式,不须要进行了加锁操作,会更平安,然而会减慢启动速度。

上面咱们一起来看看 go 实现饿汉模式:

type singleton struct {

}

var instance = new(singleton)

func GetInstance()  *singleton{return instance}

或者
type singleton struct {

}

var instance *singleton

func init()  {instance = new(singleton)
}

func GetInstance()  *singleton{return instance}

这两种办法都能够,第一种咱们采纳创立一个全局变量的形式来实现,第二种咱们应用 init 包加载的时候创立实例,这里两个都能够,不过依据 golang 的执行程序,全局变量的初始化函数会比包的 init 函数先执行,没有特地的差距。

小坑

还记得我结尾说的一句话,go语言中应用单例模式有一个小坑,如果不留神,就会导致咱们的单例模式没有用,能够察看一下我写的代码,除了 GetInstance 办法外其余都应用的小写字母结尾,晓得这是为什么吗?

golang 中依据首字母的大小写来确定能够拜访的权限。无论是办法名、常量、变量名还是构造体的名称,如果首字母大写,则能够被其余的包拜访;如果首字母小写,则只能在本包中应用。能够简略的了解成,首字母大写是私有的,首字母小写是公有的。这里 type singleton struct { 咱们如果应用大写,那么咱们写的这些办法就没有意义了,其余包能够通过 s := &singleton{} 创立多个实例,单例模式就显得很没有意义了,所以这里肯定要留神一下哦~~~

总结

这一篇就到此结束了,这里解说了 23 种模式中最简略的单例模式,尽管他很简略,然而越简略的越容易犯错的呦,所以肯定要仔细看待每一件事件的呦~~

好啦,这一篇就到此结束了,我的代码已上传 github:https://github.com/asong2020/…

欢送 star

结尾给大家发一个小福利吧,最近我在看 [微服务架构设计模式] 这一本书,讲的很好,本人也收集了一本 PDF,有须要的小伙能够到自行下载。获取形式:关注公众号:[Golang 梦工厂],后盾回复:[微服务],即可获取。

我翻译了一份 GIN 中文文档,会定期进行保护,有须要的小伙伴后盾回复 [gin] 即可下载。

我是 asong,一名普普通通的程序猿,让我一起缓缓变强吧。我本人建了一个 golang 交换群,有须要的小伙伴加我vx, 我拉你入群。欢送各位的关注,咱们下期见~~~

举荐往期文章:

  • 手把手教姐姐写音讯队列
  • 详解 Context 包,看这一篇就够了!!!
  • go-ElasticSearch 入门看这一篇就够了(一)
  • 面试官:go 中 for-range 应用过吗?这几个问题你能解释一下起因吗
  • 学会 wire 依赖注入、cron 定时工作其实就这么简略!
  • 据说你还不会 jwt 和 swagger- 饭我都不吃了带着实际我的项目我就来了
  • 把握这些 Go 语言个性,你的程度将进步 N 个品位(二)
  • go 实现多人聊天室,在这里你想聊什么都能够的啦!!!
  • grpc 实际 - 学会 grpc 就是这么简略
  • go 规范库 rpc 实际
  • 2020 最新 Gin 框架中文文档 asong 又捡起来了英语,用心翻译
  • 基于 gin 的几种热加载形式
  • boss: 这小子还不会应用 validator 库进行数据校验,开了~~~

正文完
 0