定义

单例设计模式(Singleton Design Pattern):在应用作用域中,类只允许创建一个实例(对象)

如何实现一个单例?

实现一个单例,需要关注下面几个要点:

  • 构建函数需要是私有权限的,这样才能规避外部调用new创建实例;
  • 考虑对象创建时的线程安全问题;
  • 单例实例创建是否支持延迟加载
  • 考虑getInstance()性能设计时候,根据应用场景考虑是否嵌入加锁

1. 饿汉式创建单例

饿汉式创建单例方式,在类初始化的时候,完成instance实例的创建并完成初始化,所以instance实例的创建过程是线程安全的.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
var inStance idGenerator

type idGenerator struct {
}

func init() {
	rand.Seed(time.Now().UnixNano())
	inStance = idGenerator{}
}

func GetInstance() idGenerator {
	return inStance
}

func (id idGenerator) GetId() uint64 {
	return rand.Uint64()
}
  • 资源:如果实例占用资源多,提前初始化实例是浪费资源的行为。最好是等到程序需要用的时候再去初始化,按照fail-fast的设计原则(有问题早发现早处理),如果资源不够,在程序启动的时候就触发报错
  • 耗时:如果存在初始化过程比较耗时,我们可以在程序启动的时候,将耗时初始化操作,提前放到程序启动的时候完成。
  • 延迟加载:不支持

2. 懒汉式创建实例

懒汉式相较于饿汉式的创建实例的优势,在于支持延时加载

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
var inStance *idGenerator
var m sync.Mutex

type idGenerator struct {
}

func GetInstance() *idGenerator {
	if inStance == nil {
		m.Lock()
		if inStance == nil {
			inStance = &idGenerator{}
		}
		m.Unlock()
	}
	return inStance
}

func (id idGenerator) GetId() uint64 {
	return rand.Uint64()
}

思考:

  • 如何理解单例模式中的唯一性?
  • 如何实现线程唯一的单例?
  • 如何实现集群环境下的单例?
  • 如何实现一个多例模式?