单例模式的核心目标是确保某个类型的实例在程序生命周期内只存在一份,在Golang中通常通过包级变量、同步机制等方式实现,不同的实现方式在并发安全、初始化时机等方面存在差异。

Golang单例模式的常见实现方式
1. 饿汉式实现
饿汉式是在程序启动时就完成实例的初始化,利用Golang包初始化的特性实现,天然支持并发安全。
package singleton
// 定义单例结构体
type Config struct {
AppName string
Port int
}
// 包级变量,程序启动时初始化
var configInstance = &Config{
AppName: "demo_app",
Port: 8080,
}
// 获取实例的方法
func GetConfigInstance() *Config {
return configInstance
}
这种方式的优点是实现简单,无并发问题,缺点是如果实例初始化成本高且程序全程未使用该实例,会造成资源浪费。
2. 基础懒汉式实现
懒汉式是在第一次调用获取实例的方法时才初始化实例,非并发场景下可以正常工作,但并发场景下会出现问题。
package singleton
type Database struct {
Dsn string
}
var dbInstance *Database
func GetDBInstance() *Database {
// 非并发场景下,首次调用时初始化
if dbInstance == nil {
dbInstance = &Database{
Dsn: "root:password@tcp(127.0.0.1:3306)/test",
}
}
return dbInstance
}
多个goroutine同时调用GetDBInstance时,可能会同时判断dbInstance为nil,导致创建多个实例,不满足单例要求。
3. 双重检查锁实现
为了解决基础懒汉式的并发问题,可以加入互斥锁和双重检查逻辑,既保证并发安全,又减少锁的开销。
package singleton
import "sync"
type Cache struct {
Data map[string]string
}
var (
cacheInstance *Cache
cacheLock sync.Mutex
)
func GetCacheInstance() *Cache {
// 第一次检查,避免每次调用都加锁
if cacheInstance == nil {
cacheLock.Lock()
defer cacheLock.Unlock()
// 第二次检查,防止多个goroutine同时进入第一个判断后重复创建
if cacheInstance == nil {
cacheInstance = &Cache{
Data: make(map[string]string),
}
}
}
return cacheInstance
}
这种方式在并发场景下安全,但实现逻辑相对复杂,还需要注意内存可见性问题,在部分场景下可能存在隐患。
4. 基于sync.Once的实现
Golang标准库中的sync.Once可以保证某个操作在程序运行期间仅执行一次,是实现单例模式的推荐方式。
package singleton
import "sync"
type Logger struct {
LogPath string
}
var (
loggerInstance *Logger
once sync.Once
)
func GetLoggerInstance() *Logger {
// once.Do中的函数只会执行一次
once.Do(func() {
loggerInstance = &Logger{
LogPath: "/var/log/app.log",
}
})
return loggerInstance
}
这种方式实现简洁,由标准库保证并发安全和执行次数,是Golang中实现单例模式的最优选择。
不同实现方式的对比
| 实现方式 | 并发安全 | 初始化时机 | 实现复杂度 | 适用场景 |
|---|---|---|---|---|
| 饿汉式 | 是 | 程序启动 | 低 | 实例初始化成本低,程序大概率会使用 |
| 基础懒汉式 | 否 | 首次调用 | 低 | 非并发场景,不推荐使用 |
| 双重检查锁 | 是 | 首次调用 | 高 | 需要懒加载且对性能要求极高的特殊场景 |
| sync.Once实现 | 是 | 首次调用 | 低 | 大多数需要懒加载的单例场景 |
实现单例模式的注意事项
- 单例结构体尽量不要暴露导出字段,避免外部直接修改实例状态,可通过方法提供访问入口。
- 如果单例实例需要释放资源,需要额外实现销毁逻辑,避免内存泄漏。
- 不要在单例的初始化逻辑中依赖其他未初始化的单例,避免出现循环依赖问题。
单例模式虽然好用,但不要过度使用,如果一个类型的实例不需要全局唯一,强行使用单例反而会增加代码的耦合度,不利于测试和维护。
Golang单例模式go_singletonsync.Once并发安全修改时间:2026-07-15 07:09:25