单例模式
单例模式(Singleton)是一种非常简单且容易理解的设计模式。顾名思义,单例即单一的实例,确切地讲就是指在某个系统中只存在一个实例,同时提供集中、统一的访问接口,以使系统行为保持协调一致。singleton一词在逻辑学中指“有且仅有一个元素的集合”,这非常恰当地概括了单例的概念,也就是“一个类仅有一个实例”。
饿汉模式
Java版
“private”关键字确保太阳实例的私有性、不可见性和不可访问性;而“static”关键字确保太阳的静态性,将太阳放入内存里的静态区,在类加载的时候就初始化了,它与类同在,也就是说它是与类同时期且早于内存堆中的对象实例化的,该实例在内存中永生,内存垃圾收集器(Garbage Collector,GC)也不会对其进行回收;“final”关键字则确保这个太阳是常量、恒量,它是一颗终极的恒星,引用一旦被赋值就不能再修改;最后,“new”关键字初始化太阳类的静态实例,并赋予静态常量sun。这就是“饿汉模式”(eager initialization),即在初始阶段就主动进行实例化,并时刻保持一种渴求的状态,无论此单例是否有人使用
public class Sun {
private static final Sun sun = new Sun();
private Sun(){
//构造方法私有
}
public static Sun getInstance(){
return sun;
}
}
Go版
singleton 包在被导入时会自动初始化 instance 实例,使用时通过调用 singleton.GetSingleton () 函数即可获得 singleton 这个结构体的单例对象。需要注意的是,尽管饿汉式实现单例模式的方式简单,但大多数情况下并不推荐。因为如果单例实例化时初始化内容过多,会造成程序加载用时较长。
package singleton
type singleton struct{}
var instance = &singleton{}
func GetSingleton() *singleton {
return instance
}
懒汉模式
Java版
public class Sun {
private volatile static Sun sun;
private Sun(){}
public static Sun getInstance(){
if (sun==null){
synchronized (Sun.class){
if (sun==null){
sun = new Sun();
}
}
}
return sun;
}
}
Go
在Go语言中,我们有两种饿汉模式的实现方法,一种是模仿Java中的写法
还有一种我们可以使用Go中sync包中的Once
Once 是一个结构体,在执行 Do 方法的内部通过 atomic 操作和加锁机制来保证并发安全,且 once.Do 能够保证多个 goroutine 同时执行时 &singleton {} 只被创建一次。
package singleton
import "sync"
type singleton2 struct{}
var instance2 *singleton2
var mu sync.Mutex
func GetSingleton2() *singleton2 {
if instance2 == nil {
mu.Lock()
defer mu.Unlock()
if instance2 == nil {
instance2 = &singleton2{}
}
}
return instance2
}
package singleton
import "sync"
type singleton3 struct{}
var instance3 *singleton3
var once sync.Once
func GetSingleton3() *singleton3 {
once.Do(func() {
instance3 = &singleton3{}
})
return instance3;
}
相比“懒汉模式”,其实在大多数情况下我们通常会更多地使用“饿汉模式”,原因在于这个单例迟早是要被实例化占用内存的,延迟懒加载的意义并不大,加锁解锁反而是一种资源浪费,同步更是会降低CPU的利用率,使用不当的话反而会带来不必要的风险