Go 语言单例模式详解
单例模式是一种常见的设计模式,确保一个类只有一个实例,并提供全局访问点获取该实例。在 Go 语言中,可通过饱汉式和饿汉式实现单例模式,以下为详细介绍。
一、饿汉式单例模式
1. 实现原理
饿汉式单例模式在程序启动时就创建单例实例,无论是否使用该实例。其优点是实现简单且线程安全,缺点是可能造成资源浪费,因为即使实例未被使用,也会在启动时创建。
2. 代码示例
go
package main
import "fmt"
// Singleton 定义单例结构体
type Singleton struct{}
// instance 全局单例实例,在包初始化时就创建
var instance = &Singleton{}
// GetInstance 获取单例实例的函数
func GetInstance() *Singleton {
return instance
}
func main() {
s1 := GetInstance()
s2 := GetInstance()
if s1 == s2 {
fmt.Println("两个实例是相同的,单例模式生效")
} else {
fmt.Println("单例模式未生效")
}
}
在上述代码中,instance
变量在包初始化时创建,GetInstance
函数直接返回该实例。
二、饱汉式单例模式
1. 实现原理
饱汉式单例模式在第一次使用时才创建单例实例,避免不必要的资源浪费。在多线程环境下,需考虑线程安全,确保实例仅创建一次。Go 语言中可使用 sync.Once
实现线程安全的饱汉式单例模式。
2. 代码示例
go
package main
import (
"fmt"
"sync"
)
// Singleton 定义单例结构体
type Singleton struct{}
// instance 全局单例实例,初始值为 nil
var instance *Singleton
var once sync.Once
// GetInstance 获取单例实例的函数
func GetInstance() *Singleton {
once.Do(func() {
instance = &Singleton{}
})
return instance
}
func main() {
s1 := GetInstance()
s2 := GetInstance()
if s1 == s2 {
fmt.Println("两个实例是相同的,单例模式生效")
} else {
fmt.Println("单例模式未生效")
}
}
once.Do
方法确保传入的函数只执行一次,保证 instance
仅创建一次,实现线程安全的饱汉式单例模式。
三、单例结构体中的字段参数
单例结构体的字段参数用于存储和管理单例实例的状态信息,以下结合饿汉式和饱汉式介绍使用方法。
1. 饿汉式单例模式下的字段参数
go
package main
import "fmt"
// Config 单例结构体,包含应用程序的配置信息
type Config struct {
AppName string
Version string
}
// instance 全局单例实例,在包初始化时就创建
var instance = &Config{
AppName: "MyApp",
Version: "1.0.0",
}
// GetInstance 获取单例实例的函数
func GetInstance() *Config {
return instance
}
func main() {
config := GetInstance()
fmt.Printf("App Name: %s, Version: %s\n", config.AppName, config.Version)
}
Config
结构体包含 AppName
和 Version
字段,包初始化时创建 instance
并初始化字段,通过 GetInstance
函数获取实例并访问字段。
2. 饱汉式单例模式下的字段参数
go
package main
import (
"fmt"
"sync"
)
// Config 单例结构体,包含应用程序的配置信息
type Config struct {
AppName string
Version string
}
// instance 全局单例实例,初始值为 nil
var instance *Config
var once sync.Once
// GetInstance 获取单例实例的函数
func GetInstance() *Config {
once.Do(func() {
instance = &Config{
AppName: "MyApp",
Version: "1.0.0",
}
})
return instance
}
func main() {
config := GetInstance()
fmt.Printf("App Name: %s, Version: %s\n", config.AppName, config.Version)
}
once.Do
确保 Config
实例仅创建一次,并初始化 AppName
和 Version
字段,通过 GetInstance
函数获取实例并访问字段。
3. 修改单例结构体字段
单例结构体字段可在获取实例后修改,但多协程环境下需注意并发安全,建议使用同步机制(如互斥锁 sync.Mutex
)。
go
package main
import (
"fmt"
"sync"
)
// Config 单例结构体,包含应用程序的配置信息
type Config struct {
AppName string
Version string
Mutex sync.Mutex
}
// instance 全局单例实例,初始值为 nil
var instance *Config
var once sync.Once
// GetInstance 获取单例实例的函数
func GetInstance() *Config {
once.Do(func() {
instance = &Config{
AppName: "MyApp",
Version: "1.0.0",
}
})
return instance
}
// UpdateVersion 更新单例结构体中 Version 字段的函数
func (c *Config) UpdateVersion(newVersion string) {
c.Mutex.Lock()
defer c.Mutex.Unlock()
c.Version = newVersion
}
func main() {
config := GetInstance()
fmt.Printf("Before update - App Name: %s, Version: %s\n", config.AppName, config.Version)
config.UpdateVersion("2.0.0")
fmt.Printf("After update - App Name: %s, Version: %s\n", config.AppName, config.Version)
}
Config
结构体添加 Mutex
字段用于并发控制,UpdateVersion
方法修改 Version
字段时先加锁后解锁,确保线程安全。