1 简介
单例模式保证了一个类的对象只存在一个,同时维护了一个对其对象的全局共享访问点
2 使用场景
- 某个类对于所有客户端只有一个可用实例。如果该对象已经被创建,则返回已有对象
- 需要严格地控制全局变量。可以保证一个类只存在一个实例,无法通过其它方式替换缓存的实例
- 实现计数器类。保证同步数据
- 日志记录对象。共享的日志读写只能由一个实例操作,保证内容追加的一致性
- 程序的配置对象。保证共享对象的一致性
- 数据库连接池。节省数据库连接引起的效率损耗
- 多线程线程池。方便对线程池的线程进行控制
3 实现方式
3.1 懒汉式
创建对象时不直接创建对象,在需要使用时再创建
// 对象
type singleton struct {
}
var instance *singleton // 全局单例对象
func GetInstance() *singleton {
// 为空代表还没有线程创建该对象
if instance == nil {
instance = &singleton{} // 创建对象
}
return instance
}
3.2 饿汉式
直接创建对象,导入包的同时马上创建对象,会持续存储在内存中
// 对象
type singleton struct {
}
var instance *singleton // 全局单例对象
func init() {
// 直接创建该对象
if instance == nil {
instance = &singleton{}
}
}
func GetInstance() *singleton {
return instance
}
3.3 双重检查
在懒汉式基础上增加锁,并优化锁操作,保证并发安全且不影响性能
// 对象
type singleton struct {
}
var (
instance *singleton // 全局单例对象
mtx sync.Mutex // 对象锁
)
func GetInstance() *singleton {
// 判断是否已有对象,已有则不需要加锁判断
if instance == nil {
// 加锁判断,防止已经有线程在创建对象了
mtx.Lock()
defer mtx.Unlock()
// 加锁后,此时为空则代表还没有线程创建该对象
if instance == nil {
instance = &singleton{} // 创建对象
}
}
return instance
}
3.4 sync.Once
使用Go标准库提供的只执行一次的函数实现。与饿汉式的区别在于,init函数在导包时就把对象生成了,不及时使用就会浪费内存空间,sync.Once可以保证需要使用时,全局只创建对象一次
// 对象
type singleton struct {
}
var (
instance *singleton // 全局单例对象
once sync.Once
)
func GetInstance() *singleton {
once.Do(func() {
instance = &singleton{} // 创建对象
})
return instance
}
4 优缺点分析
4.1 优点
- 单例模式可以扩展加入工厂模式中使用
- 系统内存只存在一个对象,可以避免频繁创建和销毁的消耗,提升性能
4.2 缺点
- 非抽象形式,可扩展性较低
- 容易导致共享连接池对象过多,且没有被释放的情况,从而导致连接池溢出;对象长时间没有使用,可能在GC时被认为是垃圾对象而被回收,造成对象丢失