229. 【go 语言】go 语言的单例(Singleton)模式

一、简介

单例模式(Singleton Pattern)规定,一个类只允许有一个实例,而且自行实例化并向整个系统提供这个实例。因此单例模式的特点如下:

  • 只有一个实例
  • 必须自行创建
  • 必须自行向整个系统提供这个实例

单例模式主要用于避免频繁地创建于销毁一个全局使用的类。当想要控制实例的数量,或不允许存在多个实例时,单例模式就派上用场了。

二、Go 语言实现

1. 饿汉单例模式

package go_01

/**
* @Author qijing
* @Description 饿汉单例模式
* @Date 2022-02-26 15:09
* @Param
* @return
**/
type singleton struct {
    count int
}

var Instance = new(singleton)

func (s *singleton) Add() int {
    s.count++
    return s.count
}

代码说明:

  • 类(结构体 singleton)本身非公开(小写字母开头,非导出)。
  • 没有提供导出的 getInstance 工厂方法(Go 没有静态方法),而是直接提供了包级导出变量 Instance。

2. 懒汉单例模式

package go_01

import "sync"

/**
* @Author qijing
* @Description 懒汉式单例模式
* @Date 2022-02-26 15:30
* @Param
* @return
**/
type singletonLazy struct {
    count int
}
var (
    instance *singletonLazy
    mutex sync.Mutex
)

func New() *singletonLazy {
    mutex.Lock()
    if instance == nil {
        instance = new(singletonLazy)
    }
    mutex.Unlock()

    return instance
}

// 双重检查版本
func New2() *singletonLazy {
    if instance == nil {
        mutex.Lock()
        if instance == nil {
            instance = new(singletonLazy)
        }
        mutex.Unlock()
    }
    return instance
}
func (s *singletonLazy) Add() int {
    s.count++
    return s.count
}

代码说明:

  • 包级变量变为非导出(instance)。注意这里类型应该使用指针,因为结构体的默认值不是 nil。
  • 提供了工厂方法,按照 Go 的管理,这里命名为 New()。
  • 多 goroutine 保护,对应 Java 的 synchorized,Go 使用 sync.Mutex。

3. 懒汉单例模式 vs “双重检查”懒汉单例模式

测试代码:

// 懒汉单例模式
func BenchmarkSingletonLazy_New(b *testing.B) {
    for i := 0; i < b.N; i++ {
        New()
    }
}

// ”双重检查“懒汉单例模式
func BenchmarkSingletonLazy_New2(b *testing.B)  {
    for i := 0; i < b.N; i++ {
        New2()
    }
}

执行测试:go test -bench=.

测试结果:


测试结果

测试结果分析:
从测试结果看“双重检查”懒汉模式(New2())比懒汉单例模式(New())快十几倍。

4. “双重检查”

这里的“双重检查”,其实是 C 语言的一种代码模式。

在双重检查版本中,在 instance 实例化后,锁永远不会被执行,这就是 New2()New() 快十几倍的原因。

你可能感兴趣的:(229. 【go 语言】go 语言的单例(Singleton)模式)