单例模式的推导

1、核心思想

保证一个类永远只能有一个对象、且该对象的功能依然能被其他模块使用

2、单例模式的逻辑推演

什么是单例模式呢、其实开篇第一句话已经讲的很清楚了、但是对于某些同学来说可能有点难理解、要如何保证一个类永远只能有一个对象、但是这个对象的功能又能被其他模块使用呢?

只要你遵循下面例子中的三个要点就可以了

go 复制代码
package main

import "fmt"

/*
三个要点:
  1.某个类只能有一个实例
  2.它/须自行创建这个实例
  3.它须自行向整个系统提供这个实例。保证一个类永远只能有一个对象
*/


// 1.如果这个类是首字母大写的话,这个类就可以被外界用来创建对象、就不符合第二个要点、可能会创建多个对象、所以这个类应该是非共有访问、首字母需要小写
type singelton struct {
}

// 2.还要有一个指针指向这个唯一对象、但是这个指针永远不能改变方法、这个其实是为了符合第一个要点
// golang中没有常指针概念、因此只能通过将这个指针私有化不让外部模块访问、所以指针也是小写的
var instance *singelton = new(singelton)

// 3.如果全部私有化、那么外部模块就无法访问到这个对象、所以需要对外提供一个方法来获取这个对象、很显然这个符合第三个要点、保证它可能向整个系统提供
func GetInstance() *singelton {
  return instance
}

//4.这个时候我们就可以为对象来创建方法了
func (s *singelton) Echo() {
  fmt.Println("单例的方法")
}

func main() {
  s1 := GetInstance()
  s1.Echo()

  s2 := GetInstance()

  if s1 == s2 {
    fmt.Println("s1 == s2")
  }
}

//输出结果是
//单例的方法
//s1 == s2
//当我们第二次去调用GetInstance()的时候、实际上返回的还是刚开始就创建好的、感兴趣的小伙伴可以自己试一下

3、懒汉式与饿汉式的区别

单例模式分为两种、第一种是饿汉式单例、第二种是懒汉式单例、那么这两种有什么区别呢?

直接告诉你答案吧

饿汉式单例模式是一开始就创建好一个对象、你每次调用的时候用的都是那个已经创建好的

懒汉式单例模式呢、是当你调用的时候才会开始创建、然后其他人再调用的时候用第一个人创建的

看一下下面的例子加深一下理解吧

go 复制代码
1.懒汉式是在被调用的时候才会去生成对象
func GetInstance1() *singelton1 {
    if instance1 == nil {  
        instance1 = new(singelton1)
    }
    return instance1
}

2.饿汉式是直接生成对象、调用的时候返回
var instance *singelton = new(singelton)

func GetInstance() *singelton {
  return instance
}

4、懒汉式单例与并发安全

懒汉式单例有一个弊端、就是如果同时有多个用户同时获取单例、他们得到的结果都是还没有创建、结果每个人都创建了一个、这样肯定是不对的、那么有什么办法可以避免这种现象的发生呢

我们有三种解决办法

1、加互斥锁

既然多个用户可能同时创建、那我在创建的时候锁起来只运行一个人创建不就好了

go 复制代码
package main

import (
  "fmt"
  "sync"
  "sync/atomic"
)


type singelton1 struct {
}


var (
  lock        sync.Mutex
  instance1   *singelton1
  initialized uint32
)

//1.第一种方式我们使用互斥锁来解决并发的问题、每次调用GetInstance1都会加锁、并发情况下goroutine会在锁上排队、产生开销
func GetInstance1() *singelton1 {
  
  if atomic.LoadUint32(&initialized) == 1 {
    return instance1
  }
  //如果不存在、加锁申请
  lock.Lock()
  defer lock.Unlock()
  if instance1 == nil {
    instance1 = new(singelton1)
   
  }
  return instance1
}

func (s *singelton1) Echo1() {
  fmt.Println("懒汉式单例的方法")
}

func main() {
  s1 := GetInstance1()
  s1.Echo()

  s2 := GetInstance1()

  if s1 == s2 {
    fmt.Println("s1 == s2")
  }
}

// 延申1.对于懒汉式单例,如果高并发的情况下有可能创建多个对象、需要考虑线程安全、可以加个锁

2、使用原子标记

go 复制代码
package main

import (
  "fmt"
  "sync"
  "sync/atomic"
)

type singelton1 struct {
}


var (
  lock        sync.Mutex
  instance1   *singelton1
  initialized uint32
)

//1.第二种我们在原有的互斥锁基础上添加原子操作、在外层过滤已经初始化的情况、减少锁竞争
func GetInstance1() *singelton1 {
  if atomic.LoadUint32(&initialized) == 1 {
    return instance1
  }
  //如果不存在、加锁申请
  lock.Lock()
  defer lock.Unlock()
  if instance1 == nil {
    instance1 = new(singelton1)
    // 设计标记为1
    atomic.StoreUint32(&initialized, 1)
  }
  return instance1
}

func (s *singelton1) Echo1() {
  fmt.Println("懒汉式单例的方法")
}

func main() {
  s1 := GetInstance1()
  s1.Echo()

  s2 := GetInstance1()

  if s1 == s2 {
    fmt.Println("s1 == s2")
  }
}

// 延申2.如果每次获取这个单例的时候都加个锁、会比较慢、性能不好、可以使用原子标记

3、sync.Once

go 复制代码
package main

import (
  "fmt"
  "sync"
  "sync/atomic"
)

type singelton2 struct {
}


var (
    instance2 *singleton2
    once     sync.Once
)

func GetInstance2() *singleton2 {
    once.Do(func() {
        instance2 = &singleton{}
    })
    return instance
}

func (s *singelton2) Echo2() {
  fmt.Println("懒汉式单例的方法")
}

func main() {
  s1 := GetInstance1()
  s1.Echo()

  s2 := GetInstance1()

  if s1 == s2 {
    fmt.Println("s1 == s2")
  }
}
//sync包是Go语言标准库中的一个类型,它提供了一种机制来确保某个操作只执行一次、既简单又好用
//引申:有兴趣的同学可以自行查询sync.once的实现原理、是通过atomic和mutex组合实现

好了、我们今天的分享就到这里、如果小伙伴们有任何的意见或者建议、欢迎评论与我分享!!

相关推荐
JIngJaneIL13 分钟前
基于springboot + vue古城景区管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot·后端
小信啊啊35 分钟前
Go语言切片slice
开发语言·后端·golang
Victor3562 小时前
Netty(20)如何实现基于Netty的WebSocket服务器?
后端
缘不易2 小时前
Springboot 整合JustAuth实现gitee授权登录
spring boot·后端·gitee
Kiri霧2 小时前
Range循环和切片
前端·后端·学习·golang
WizLC3 小时前
【Java】各种IO流知识详解
java·开发语言·后端·spring·intellij idea
Victor3563 小时前
Netty(19)Netty的性能优化手段有哪些?
后端
爬山算法3 小时前
Netty(15)Netty的线程模型是什么?它有哪些线程池类型?
java·后端
白宇横流学长3 小时前
基于SpringBoot实现的冬奥会科普平台设计与实现【源码+文档】
java·spring boot·后端
Python编程学习圈4 小时前
Asciinema - 终端日志记录神器,开发者的福音
后端