【设计模式】创建型模式之单例模式(Golang实现)

简介: 【2月更文挑战第3天】一个类只允许创建一个对象或实例,而且自行实例化并向整个系统提供该实例,这个类就是一个单例类,它提供全局访问的方法。这种设计模式叫单例设计模式,简称单例模式。

定义

一个类只允许创建一个对象或实例,而且自行实例化并向整个系统提供该实例,这个类就是一个单例类,它提供全局访问的方法。这种设计模式叫单例设计模式,简称单例模式。

单例模式的要点:

  1. 某个类只能有一个实例
  2. 必须自行创建该实例
  3. 必须自行向整个系统提供该实例

时序图:

用处

从业务概念上来看,有些数据在系统中只应该保留一份,就比较适合设计为单例类。比如,系统的配置信息。

使用场景:

  • 系统只需要一个实例对象,比如唯一的序列号生成器(业务方面),或是考虑到资源消耗太大而只允许创建一个对象
  • 客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点外,不能通过其他途径访问该实例

优缺点

优点:

  • 提供了对唯一实例的受控访问
  • 因为在系统的内存里只存在一个对象,所以可以节约系统资源,尤其是一些需要频繁创建和销毁的对象
  • 允许可变数目的实例,基于单例模式进行扩展

缺点:

  • 没有抽象层,很难扩展
  • 职责过重,在一定程度上违背了“单一职责原则”。因为单例类既充当了工厂角色,提供了工厂方法,同时又充当了产品角色,包含一些业务方法,将产品的创建和产品的本身的功能融合到一起。

单例模式的唯一性

  1. 进程间唯一:默认单例模式的唯一性就是基于进程的唯一性。因为编写的代码成为可执行文件后,当运行该可执行文件时,操作系统会启动一个进程,将该可执行文件从磁盘加载到自己的进程地址空间,该进程依次执行文件中的代码,比如代码里有一个创建student对象的语句,进程就会在地址空间里创建一个student对象。如果在一个进程中创建另一个进程,操作系统会给新进程分配新的地址空间,而且将旧地址空间的内容拷贝一份,包括代码和数据,这样新进程里有且只有一个student对象,旧进程里也有且只有一个student,但是这两个对象并不是同一个对象。
  2. 线程间唯一:通过获取线程id来实现。但是在golang里主要使用协程,而且协程的id并不会暴露出来。
  3. 集群环境间唯一(多进程):通过外部共享存储的锁进行,如文件。将单例对象序列化后存储到外部共享存储区里(比如文件),进程在使用该单例对象的时候,需要对该单例对象加锁,避免其他进程再获取,然后将该对象加载到内存里,反序列化为单例对象,使用完后还要从内存里删除,再存储回外部共享存储区,并释放锁。

如何实现

  • 构造函数是private访问权限
  • 考虑对象创建时的线程安全问题
  • 考虑是否支持延迟加载
  • 考虑getInstance的性能问题,比如是否有加锁等

实现方式

饿汉式

在类加载的时候实例就已经创建好了,实例的创建过程线程安全,不支持延迟加载

有两种实现方式,第一种是定义全局变量的时候创建实例,第二种是采用包的init函数创建实例

注意这里的Singleton类型也要是大写字母开头的,因为GetInstance方法是大写字母开头,表明包外可访问,那么该方法的返回值也需要包外可访问。

package singleton

// 单例模式 饿汉式实现
type Singleton struct{
   }

var singleton *Singleton

//1.全局变量的实现方式
//var singleton1 = &Singleton{}
//2. 包的init函数实现方式
func init() {
   
    singleton = &Singleton{
   }
}

func GetInstance() *Singleton {
   
    return singleton
}

懒汉式

在获取实例的时候再去创建,实例创建过程需要加锁,支持延迟加载,不支持高并发

不加锁

只是对懒汉式创建的一个理解,在GetInstnce方法里判断singleton是否为空,为空的话就去创建一个实例,否则直接返回该实例。

存在线程安全问题,高并发的时候会创建多个对象,不推荐使用。

package singleton

// 单例模式 懒汉式实现 不加锁
type Singleton struct{
   }

var singleton *Singleton

func GetInstance() *Singleton {
   
    if singleton == nil {
   
        singleton = &Singleton{
   }
    }
    return singleton
}

给方法加锁

GetInstance整个方法进行加锁,确保并发安全,但是每一个对象创建的时候都需要进行加锁解锁,效率低

package singleton

import "sync"

// 单例模式 懒汉式实现 方法锁
type Singleton struct{
   }

var singleton *Singleton
var mu sync.Mutex

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

双重检测

在懒汉式的基础上,将方法的锁改为类级别的锁,相对于懒汉式的粒度更小,不用每次都去获取锁

借助sync.Once确保只创建一次

package singleton

import "sync"

// 单例模式 懒汉式实现 双重检测
type LazySingleton struct{
   }

var lazySingleton *LazySingleton
var once = &sync.Once{
   }

func GetLazyInstance() *LazySingleton {
   
    if lazySingleton == nil {
   
        once.Do(func() {
   
            lazySingleton = &LazySingleton{
   }
        })
    }
    return lazySingleton
}

静态内部类

java的静态内部类,线程安全,延迟加载

枚举

java的枚举特性

测试性能

测试饿汉式的init实现方法和懒汉式的sync.Once实现方法

package singleton

import "testing"

func BenchmarkGetInstanceParallel(b *testing.B) {
   
    b.RunParallel(func(pb *testing.PB) {
   
        for pb.Next() {
   
            if GetInstance() != GetInstance() {
   
                b.Errorf("test fail")
            }
        }
    })
}

func BenchmarkGetLazyInstanceParallel(b *testing.B) {
   
    b.RunParallel(func(pb *testing.PB) {
   
        for pb.Next() {
   
            if GetLazyInstance() != GetLazyInstance() {
   
                b.Errorf("test fail")
            }
        }
    })
}

在对应的目录下执行:

go test -bench='Parallel$' -benchmem .

可以看出饿汉式的性能更好一点

参考链接
图解设计模式
Go设计模式
Golang单例模式

目录
相关文章
|
2月前
|
设计模式 安全 测试技术
【C/C++ 设计模式 单例】单例模式的选择策略:何时使用,何时避免
【C/C++ 设计模式 单例】单例模式的选择策略:何时使用,何时避免
62 0
|
2月前
|
设计模式 缓存 安全
【设计模式】单例模式:确保类只有一个实例
【设计模式】单例模式:确保类只有一个实例
23 0
|
3天前
|
设计模式 安全 Java
【JAVA】Java 中什么叫单例设计模式?请用 Java 写出线程安全的单例模式
【JAVA】Java 中什么叫单例设计模式?请用 Java 写出线程安全的单例模式
|
2月前
|
设计模式 安全 Java
设计模式之单例模式
设计模式之单例模式
|
16天前
|
设计模式 存储 Java
Java设计模式:解释一下单例模式(Singleton Pattern)。
`Singleton Pattern`是Java中的创建型设计模式,确保类只有一个实例并提供全局访问点。它通过私有化构造函数,用静态方法返回唯一的实例。类内静态变量存储此实例,对外仅通过静态方法访问。
16 1
|
2月前
|
设计模式 存储 缓存
设计模式之单例模式(C++)
设计模式之单例模式(C++)
25 2
|
2月前
|
设计模式 算法 安全
Java设计模式---创建型模式
Java设计模式---创建型模式
|
2月前
|
设计模式 安全 Java
Java设计模式之单例模式
在软件工程中,单例模式是一种常用的设计模式,其核心目标是确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。Java作为一门广泛使用的编程语言,实现单例模式是面试和实际开发中的常见需求。
66 9
Java设计模式之单例模式
|
4月前
|
设计模式 安全 Java
【设计模式】单例模式
【1月更文挑战第27天】【设计模式】单例模式
|
22天前
|
设计模式 SQL 算法
设计模式了解哪些,模版模式
设计模式了解哪些,模版模式
21 0