推荐使用 sync.Once 实现线程安全单例,它保证初始化函数仅执行一次且并发安全;避免双重检查锁,因 Go 内存模型不支持 volatile 且 sync.Once 更可靠高效;支持懒汉式与饿汉式两种方式。
在 Go 语言中实现线程安全的单例模式,核心是避免多次初始化、防止竞态,并兼顾性能与简洁性。推荐使用 sync.Once —— 它天然保证函数只执行一次,且并发安全,无需手动加锁。
sync.Once 是 Go 标准库提供的轻量级同步原语,适合单次初始化场景。它内部已做充分优化,比手写互斥锁更高效、更可靠。
instance *Singleton)和一个 sync.Once 实例GetInstance()),内部调用 once.Do() 包裹初始化逻辑示例代码:
package singleton
import "sync"
type Singleton struct {
// 可添加字段,如配置、连接池等
}
var (
instance *Singleton
once sync.Once
)
func GetInstance() *Singleton {
once.Do(func() {
instance = &Singleton{}
// 此处可加入耗时初始化操作(如加载配置、建立数据库连接)
})
return in
stance
}
有些开发者尝试模仿 Java 的 DCL 写法(先判空 → 加锁 → 再判空),但在 Go 中既不必要也不推荐:
volatile 或显式屏障,而 Go 没有 volatile
sync.Once 已由标准库严格验证,正确处理了内存可见性和执行顺序Go 不支持构造函数重载,但可通过闭包或初始化函数封装参数。关键仍是确保“仅初始化一次”:
sync.Once 控制执行时机例如:
var (
instance *Singleton
once sync.Once
config Config // 外部配置,需提前设置
)
func InitWithConfig(c Config) *Singleton {
once.Do(func() {
instance = &Singleton{config: c}
})
return instance
}
若实例创建开销小、无依赖外部资源,也可用包级变量直接初始化:
var instance = &Singleton{}
func GetInstance() *Singleton {
return instance
}
这种方式天然线程安全(变量初始化在 init() 阶段完成,由 Go 运行时保证),但缺乏懒加载特性,不适合初始化成本高或依赖运行时环境的场景。