主要区别在于 getInstance 的实现,要注意 synchronized ,避免多线程时出现问题 。
3、单例模式的 Go 实现在 Go 语言中如何实现单例模式,类比 Java 代码实现 。
// 饿汉式单例模式package singletontype singleton struct { count int}var Instance = new(singleton)func (s *singleton) Add() int { s.count++ return s.count}
前面说了,Go 只支持部分面向对象的特性,因此看起来有点不太一样:
- 类(结构体 singleton)本身非公开(小写字母开头,非导出);
- 没有提供导出的 GetInstance 工厂方法(Go 没有静态方法),而是直接提供包级导出变量 Instance;
c := singleton.Instance.Add()
看看懒汉式单例模式在 Go 中如何实现:// 懒汉式单例模式package singletonimport ( "sync")type singleton struct { count int}var ( instance *singleton mutex sync.Mutex)func New() *singleton { mutex.Lock() if instance == nil { instance = new(singleton) } mutex.Unlock() return instance}func (s *singleton) Add() int { s.count++ return s.count}
代码多了不少:- 包级变量变成非导出(instance),注意这里类型应该用指针,因为结构体的默认值不是 nil;
- 提供了工厂方法,按照 Go 的惯例,我们命名为 New();
- 多 goroutine 保护,对应 Java 的 synchronized,Go 使用 sync.Mutex;
在上面 New() 函数中,同步化(锁保护)实际上只在 instance 变量第一次被赋值之前才有用 。在 instance 变量有了值之后,同步化实际上变成了一个不必要的瓶颈 。如果能够有一个方法去掉这个小小的额外开销,不是更加完美吗?因此出现了“双重检查” 。看看 Go 如何实现“双重检查”,只看 New() 代码:
func New() *singleton { if instance == nil { // 第一次检查(①) // 这里可能有多于一个 goroutine 同时达到(②) mutex.Lock() // 这里每个时刻只会有一个 goroutine(③) if instance == nil { // 第二次检查(④) instance = new(singleton) } mutex.Unlock() } return instance}
有读者可能看不懂上面代码的意思,这里详细解释下 。假设 goroutine X 和 Y 作为第一批调用者同时或几乎同时调用 New 函数 。- 因为 goroutine X 和 Y 是第一批调用者,因此,当它们进入此函数时,instance 变量是 nil 。因此 goroutine X 和 Y 会同时或几乎同时到达位置 ①;
- 假设 goroutine X 会先达到位置 ②,并进入 mutex.Lock() 达到位置 ③ 。这时,由于 mutex.Lock 的同步限制,goroutine Y 无法到达位置 ③,而只能在位置 ② 等候;
- goroutine X 执行 instance = new(singleton) 语句,使得 instance 变量得到一个值,即对 singleton 实例的引用 。此时,goroutine Y 只能继续在位置 ② 等候;
- goroutine X 释放锁,返回 instance,退出 New 函数;
- goroutine Y 进入 mutex.Lock(),到达位置 ③,进而到达位置 ④ 。由于 instance 变量已经不是 nil,因此 goroutine Y 释放锁,返回 instance 所引用的 singleton 实例(也就是 goroutine X 锁创建的 singleton 实例),退出 New 函数;
相比前面的版本,双重检查版本,只要 instance 实例化后,锁永远不会执行了,而前面版本每次调用 New 获取实例都需要执行锁 。性能很显然,我们可以基准测试来验证:(双重检查版本 New 重命名为 New2)
package singleton_testimport ( "testing" "github.com/polaris1119/go-demo/singleton")func BenchmarkNew(b *testing.B) { for i := 0; i < b.N; i++ { singleton.New() }}func BenchmarkNew2(b *testing.B) { for i := 0; i < b.N; i++ { singleton.New2() }}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 姬昌姬发伯邑考关系 姬昌和伯邑考什么关系
- 指定扫描目标和主机发现 Nmap使用详解
- 蜡梅花茶的功效和功能,配搭功效不样的的花草茶
- 曹操和夏侯惇的关系是什么? 三国演义里的夏侯惇怎么死的
- 弓头鲸有多大 弓头鲸和座头鲸哪个大
- 红碎茶的历史和分类,广州红碎茶的特征分析
- 喝苦荞茶可以减肥吗,黑苦荞茶和黄苦荞茶哪个好
- 崂山红茶的功效和作用,黄芪红茶的功效与作用怎么样
- 小区物管应该履行哪些责任和义务
- 后背和腰疼是怎么回事