wiki百科: 单例模式,也叫单子模式,是一种常用的软件设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。

单例模式要实现的效果就是,对于应用单例模式的类,整个程序中只存在一个实例化对象

go并不是一种面向对象的语言,所以我们使用结构体来替代

有几种方式:

  • 懒汉模式

  • 饿汉模式

  • 双重检查锁机制

下面拆分讲解:

懒汉模式

  1. 构建一个示例结构体
   type example struct {
name string
}
  1. 设置一个私有变量作为每次要返回的单例
  var instance *example
  1. 写一个可以获取单例的方法
    func GetExample() *example {

    	// 存在线程安全问题,高并发时有可能创建多个对象
if instance == nil {
instance = new(example)
}
return instance
}
  1. 测试一下

      func main() {
    s := GetExample()
    s.name = "第一次赋值单例模式"
    fmt.Println(s.name) s2 := GetExample()
    fmt.Println(s2.name)
    }

懒汉模式存在线程安全问题,在第3步的时候,如果有多个线程同时调用了这个方法, 那么都会检测到instancenil,就会创建多个对象,所以出现了饿汉模式...

饿汉模式

与懒汉模式类似,不再多说,直接上代码

  // 构建一个结构体,用来实例化单例
type example2 struct {
name string
} // 声明一个私有变量,作为单例
var instance2 *example2 // init函数将在包初始化时执行,实例化单例
func init() {
instance2 = new(example2)
instance2.name = "初始化单例模式"
} func GetInstance2() *example2 {
return instance2
} func main() {
s := GetInstance2()
fmt.Println(s.name)
}

饿汉模式将在包加载的时候就创建单例对象,当程序中用不到该对象时,浪费了一部分空间

和懒汉模式相比,更安全,但是会减慢程序启动速度

双重检查机制

懒汉模式存在线程安全问题,一般我们使用互斥锁来解决有可能出现的数据不一致问题

所以修改上面的GetInstance() 方法如下:

   var mux Sync.Mutex
func GetInstance() *example {
mux.Lock()
defer mux.Unlock()
if instance == nil {
instance = &example{}
}
return instance
}

如果这样去做,每一次请求单例的时候,都会加锁和减锁,而锁的用处只在于解决对象初始化的时候可能出现的并发问题 当对象被创建之后,加锁就失去了意义,会拖慢速度,所以我们就引入了双重检查机制(Check-lock-Check), 也叫DCL(Double Check Lock), 代码如下:

  func GetInstance() *example {
if instance == nil { // 单例没被实例化,才会加锁
mux.Lock()
defer mux.Unlock()
if instance == nil { // 单例没被实例化才会创建
instance = &example{}
}
}
return instance
}

这样只有当对象未初始化的时候,才会又加锁和减锁的操作

但是又出现了另一个问题:每一次访问都要检查两次,为了解决这个问题,我们可以使用golang标准包中的方法进行原子性操作:

   import "sync"
import "sync/atomic" var initialized uint32 func GetInstance() *example { // 一次判断即可返回
if atomic.LoadUInt32(&initialized) == 1 {
return instance
}
mux.Lock()
defer mux.Unlock()
if initialized == 0 {
instance = &example{}
atomic.StoreUint32(&initialized, 1) // 原子装载
}
return instance
}

以上代码只需要经过一次判断即可返回单例,但是golang标准包中其实给我们提供了相关的方法:

sync.OnceDo方法可以实现在程序运行过程中只运行一次其中的回调,所以最终简化的代码如下:

 type example3 struct {
name string
} var instance3 *example3
var once sync.Once func GetInstance3() *example3 { once.Do(func() {
instance3 = new(example3)
instance3.name = "第一次赋值单例"
})
return instance3
} func main() {
e1 := GetInstance3()
fmt.Println(e1.name) e2 := GetInstance3()
fmt.Println(e2.name)
}

GO 单例模式的更多相关文章

  1. C++实现线程安全的单例模式

    在某些应用环境下面,一个类只允许有一个实例,这就是著名的单例模式.单例模式分为懒汉模式,跟饿汉模式两种. 首先给出饿汉模式的实现 template <class T> class sing ...

  2. 23种设计模式--单例模式-Singleton

    一.单例模式的介绍 单例模式简单说就是掌握系统的至高点,在程序中只实例化一次,这样就是单例模式,在系统比如说你是该系统的登录的第多少人,还有数据库的连接池等地方会使用,单例模式是最简单,最常用的模式之 ...

  3. angular2系列教程(十)两种启动方法、两个路由服务、引用类型和单例模式的妙用

    今天我们要讲的是ng2的路由系统. 例子

  4. java设计模式之--单例模式

    前言:最近看完<java多线程编程核心技术>一书后,对第六章的单例模式和多线程这章颇有兴趣,我知道我看完书还是记不住多少的,写篇博客记录自己所学的只是还是很有必要的,学习贵在坚持. 单例模 ...

  5. 设计模式C#合集--单例模式

    单例模式 代码: 第一种: private static Singleton singleton = null; private Singleton() { } public static Singl ...

  6. 设计模式之单例模式(Singleton)

    设计模式之单例模式(Singleton) 设计模式是前辈的一些经验总结之后的精髓,学习设计模式可以针对不同的问题给出更加优雅的解答 单例模式可分为俩种:懒汉模式和饿汉模式.俩种模式分别有不同的优势和缺 ...

  7. GOF23设计模式之单例模式

    ·核心作用: -保证一个类只有一个实例,并且提供一个访问该实例的全局访问点. ·常见应用场景: -Windows的Task Manager(任务管理器)就是很典型的单例模式 -Windows的Recy ...

  8. GJM : C#设计模式(1)——单例模式

    感谢您的阅读.喜欢的.有用的就请大哥大嫂们高抬贵手"推荐一下"吧!你的精神支持是博主强大的写作动力以及转载收藏动力.欢迎转载! 版权声明:本文原创发表于 [请点击连接前往] ,未经 ...

  9. PHP设计模式(四)单例模式(Singleton For PHP)

    今天讲单例设计模式,这种设计模式和工厂模式一样,用的非常非常多,同时单例模式比较容易的一种设计模式. 一.什么是单例设计模式 单例模式,也叫单子模式,是一种常用的软件设计模式.在应用这个模式时,单例对 ...

  10. java设计模式之单例模式(几种写法及比较)

    概念: Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记式单例. 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建 ...

随机推荐

  1. 服务器配置:ECS+Nginx+uWSGI+Flask——各部分详细介绍

    希望在阿里云ECS上搭建一个flask框架的web应用,经典的形式便是flask+uWSGI+nginx模式 服务器:CentOS 7.3 python版本:3.8.0 先贴一张全局图,这张图很清楚的 ...

  2. Flask:网页路由及请求方式的设定

    1.Flask路由的实现 Flask的路由是由route装饰器来实现的 @app.route("/index/") def index(): return "hello ...

  3. SQLserver2016对字段是json对象查询

    现在2016内置对json对象的查询支持了.正好项目中用到,做个备忘 如果字段内容是: [{"tagid":"100015","orderid&quo ...

  4. Spring Boot2 系列教程(二十)Spring Boot 整合JdbcTemplate 多数据源

    多数据源配置也算是一个常见的开发需求,Spring 和 SpringBoot 中,对此都有相应的解决方案,不过一般来说,如果有多数据源的需求,我还是建议首选分布式数据库中间件 MyCat 去解决相关问 ...

  5. 使用float设置经典的网站前端结构(深入探讨)

    .要是DIV的子元素宽度大于它自己的宽度,不管子元素有没有脱离文档流,子元素会在横向向右溢出. 关于高度:1.要是DIV的高度没有设定,其高度受“没有脱离文档流”的子元素影响.以下是DIV宽度为0的情 ...

  6. NOIP模拟 13

    我终于又厚颜无耻地赖着没走 ...... T1 矩阵游戏 用了30hmin找规律,然后发现貌似具有交换律,然后发现貌似有通项公式,然后发现貌似每次操作对通项的影响是相同的,然后发现貌似跟N没啥关系.. ...

  7. docker基本操作教程

    镜像操作 获取镜像 从Docker Hub搜索镜像: docker search ubuntu 下载镜像: docker pull ubuntu:18.04 若下载镜像速度较慢,更改镜像源: Ubun ...

  8. NOIP模拟测试13

    考得还算可以,T3还有提升空间(没看清题&&样例没过 拿了4分). 期望得分:80+40+0=120 实际得分:80+85+4=169 一脸黑线.....是数据比较水的原因,T2分都比 ...

  9. PHP对象继承

    PHP对象继承<?php class foo{ public function printItem($string) { echo 'Foo: ' . $string . PHP_EOL; } ...

  10. Jmeter与压测相关概念

    相关概念 RT(response time) 什么是RT? RT就是指系统在接收到请求和做出相应这段时间跨度 但是值得一提的是RT的值越高,并不真的就能说明我们的系统的吞吐量就很高, 比如说,如果存在 ...