创建型:单例模式


单例模式

单例模式是创建全局唯一的对象所使用的设计模式。比如,存储 log,记录时间,更新缓存的对象等等。

实现

饿汉单例

单例模式的实现,我们很自然的想到在 init 中创建私有全局变量对象,然后使用 GetObject 获取对象的方式来实现。比如:

package logger

type logger struct{}

var l *logger = nil

func GetLogger() *logger {
	return l
}

func init() {
	l = &logger{}
}

这是启动时创建单例的方式,称作饿汉单例。

因为是在启动时创建,如果单例并不需要,或者有选择性的需要,这种启动时创建就会造成一种资源的浪费,即创建了不被使用的对象。第二,如果创建对象时间较长或者使用资源较多等等会造成应用启动慢。

能否在有需要的时候创建单例对象呢?这就是所谓的懒汉单例。

懒汉单例

懒汉单例实现如下。

package logger

// private logger object
type logger struct{}

// unique object
var l *logger = nil

// Get unique object from GetLogger
func GetLogger() *logger {
	return l
}

// Create unique object
func NewLogger() *logger {
	if l == nil {
		l = &logger{}
	}

	return l
}

注意,这里的实例可以保证全局唯一,但是对于多线程单例创建无法保证全局唯一。因此,我们要给对象加锁,确保只有一个线程进入临界区创建单例。

func NewLogger() *logger {
	var m sync.Mutex

	m.Lock()
	defer m.Unlock()
	if l == nil {
		l = &logger{}
	}

	return l
}

这么实现也要问题,会导致多线程创建单例时线程处于等待锁状态,我们可以在请求锁加上单例对象判断,来保证如果当前线程获取到全局唯一对象则返回,跳过等待锁阶段。

func NewLogger() *logger {

	if l == nil {
		var m sync.Mutex
		m.Lock()
		defer m.Unlock()
		if l == nil {
			l = &logger{}
		}
	}

	return l
}

思考

  • 单例创建的两种方式中,要结合具体场景看使用哪一种。使用懒汉方式创建单例,如果创建时间较长,且该单例启动就需要的话则可以考虑饿汉单例实现,不要把创建时间较长放到使用阶段。
  • 多进程的单例,有点类似于分布式系统的集群唯一。可以使用访问全局唯一资源(类似于锁实现)来确保集群唯一。
  • Go 使用 sync.Once 创建单例。在 Go 中 sync.Once 不局限于创建单例,也包括创建全局唯一的行为。如果这个行为只应执行一次的话,那么可以使用 sync.Once

posted @   hxia043  阅读(3)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示