正文
一、简介
单例模式(Singleton Pattern)规定,一个类只允许有一个实例,而且自行实例化并向整个系统提供这个实例。因此单例模式的特点如下:
- 只有一个实例
- 必须自行创建
- 必须自行向整个系统提供这个实例
单例模式主要用于避免频繁地创建于销毁一个全局使用的类。当想要控制实例的数量,或不允许存在多个实例时,单例模式就派上用场了。
二、Go 语言实现
1. 饿汉单例模式
package go_01 /** * @Author qijing * @Description 饿汉单例模式 * @Date 2022-02-26 15:09 * @Param * @return **/ type singleton struct { count int } var Instance = new(singleton) func (s *singleton) Add() int { s.count++ return s.count }
代码说明:
- 类(结构体 singleton)本身非公开(小写字母开头,非导出)。
- 没有提供导出的 getInstance 工厂方法(Go 没有静态方法),而是直接提供了包级导出变量 Instance。
2. 懒汉单例模式
package go_01 import "sync" /** * @Author qijing * @Description 懒汉式单例模式 * @Date 2022-02-26 15:30 * @Param * @return **/ type singletonLazy struct { count int } var ( instance *singletonLazy mutex sync.Mutex ) func New() *singletonLazy { mutex.Lock() if instance == nil { instance = new(singletonLazy) } mutex.Unlock() return instance } // 双重检查版本 func New2() *singletonLazy { if instance == nil { mutex.Lock() if instance == nil { instance = new(singletonLazy) } mutex.Unlock() } return instance } func (s *singletonLazy) Add() int { s.count++ return s.count }
代码说明:
- 包级变量变为非导出(instance)。注意这里类型应该使用指针,因为结构体的默认值不是 nil。
- 提供了工厂方法,按照 Go 的管理,这里命名为 New()。
- 多 goroutine 保护,对应 Java 的 synchorized,Go 使用 sync.Mutex。
3. 懒汉单例模式 vs “双重检查”懒汉单例模式
测试代码:
// 懒汉单例模式 func BenchmarkSingletonLazy_New(b *testing.B) { for i := 0; i < b.N; i++ { New() } } // ”双重检查“懒汉单例模式 func BenchmarkSingletonLazy_New2(b *testing.B) { for i := 0; i < b.N; i++ { New2() } }
执行测试:go test -bench=.
测试结果:
测试结果
测试结果分析:
从测试结果看“双重检查”懒汉模式(New2()
)比懒汉单例模式(New()
)快十几倍。
4. “双重检查”
这里的“双重检查”,其实是 C 语言的一种代码模式。
在双重检查版本中,在 instance 实例化后,锁永远不会被执行,这就是 New2()
比 New()
快十几倍的原因。