Golang设计模式(一) | 单例模式

单例模式(Singleton Pattern),是最简单的一个模式。在 Go 中,单例模式指的是全局只有一个实例,并且它负责创建自己的对象。单例模式不仅有利于减少内存开支,还有减少系统性能开销、防止多个实例产生冲突等优点。

因为单例模式保证了实例的全局唯一性,而且只被初始化一次,所以比较适合『全局共享一个实例,且只需要被初始化一次的场景』,例如数据库实例、全局配置、全局任务池等。

单例模式又分为饿汉方式懒汉方式。饿汉方式指全局的单例实例在包被加载时创建,而懒汉方式指全局的单例实例在第一次被使用时创建。你可以看到,这种命名方式非常形象地体现了它们不同的特点。

接下来,我就来分别介绍下这两种方式。先来看饿汉方式。

饿汉方式

下面是一个饿汉方式的单例模式代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
package singleton

type singleton struct {
}

var instance *singleton = &singleton{}

func GetInstanceOr() *singleton {
    return instance
}

你需要注意,因为实例是在包被导入时初始化的,所以如果初始化操作比较耗时,会导致程序加载时间比较长。

懒汉方式

懒汉方式是开源项目中使用最多的,但它的缺点是非并发安全,在实际使用时需要加锁。以下是懒汉方式不加锁的一个实现:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package singleton

type singleton struct {
}

var instance *singleton

func GetInstanceOr() *singleton {
    if instance == nil {
		instance = &singleton{}
    }
    
    return instance
}

可以看到,在创建 instance 时,如果 instance==nil,就会再创建一个 instance 实例,这时候单例就会有多个实例。

为了解决懒汉方式并发安全的问题,需要对实例进行加锁,下面是带检查锁的一个实现:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import "sync"

type singleton struct {
}

var instance *singleton
var mu sync.Mutex

func GetInstance() *singleton {
  if instance == nil {
    mu.Lock()
    if instance == nil {
        instance = &singleton{}
    }
    mu.Unlock()
  }
  return instance
}

通过,上述代码只有在创建时才会加锁,既提高了代码效率,又保证了并发安全。

除了饿汉方式和懒汉方式,在 Golang 中,还有一种更优雅的实现方式,在实际的应用中建议采用这种方式,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package singleton

import (
    "sync"
)

type singleton struct {
}

var instance *singleton
var once sync.Once

func GetInstanceOr() *singleton {
    once.Do(func() {
		instance = &singleton{}
    })
    return instance
}

Golang的项目开发中,单例模式是使用的相对较多的一种设计模式,前文也提到了,单例保证了实例的全局唯一性,在数据库实例、全局配置、全局任务池等场景需要使用到单例模式。

本博文到此结束,祝你愉快~

comments powered by Disqus