设计模式学习-使用go实现单例模式
单例模式
定义
什么是单例模式:保证一个类仅有一个实例,并提供一个全局访问它的全局访问点。
例如:在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这样方便了读取,同时保证了我们的配置信息只会初始化一次。
优点
1、在单例模式中,活动的单例只有一个实例,对单例类的所有实例化得到的都是相同的一个实例。这样就 防止其它对象对自己的实例化,确保所有的对象都访问一个实例
2、单例模式具有一定的伸缩性,类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性
3、提供了对唯一实例的受控访问
4、由于在系统内存中只存在一个对象,因此可以节约系统资源,当需要频繁创建和销毁的对象时单例模式无疑可以提高系统的性能
5、允许可变数目的实例
6、避免对共享资源的多重占用
缺点
1、不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态
2、由于单利模式中没有抽象层,因此单例类的扩展有很大的困难
3、单例类的职责过重,在一定程度上违背了“单一职责原则”
4、滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失
适用范围
我们在项目中使用单例,都是用它来表示一些全局唯一类,比如:配置信息类、连接池类、ID生成器类。
代码实现
懒汉模式
懒汉模式也就是在需要的时候,才去创建实例对象。懒汉式相对于饿汉式的优势是支持延迟加载。不过懒汉模式不是线程安全的,需要加锁。
// 使用结构体代替类
type Tool struct {
Name string
}
// 锁对象
var lock sync.Mutex
// 建立私有变量
var instance *Tool
// 加锁保证线程安全
func GetInstance() *Tool {
lock.Lock()
defer lock.Unlock()
if instance == nil {
instance = &Tool{
Name: "我已经初始化了",
}
}
return instance
}
懒汉式的缺点也很明显,我们给getInstance()这个方法加了一把锁,导致这个函数的并发度很低。量化一下的话,并发度是1,也就相当于串行操作了。而这个函数是在单例使用期间,一直会被调用。如果这个单例类偶尔会被用到,那这种实现方式还可以接受。但是,如果频繁地用到,那频繁加锁、释放锁及并发度低等问题,会导致性能瓶颈,这种实现方式就不可取了。
饿汉模式
饿汉模式的实现方式比较简单。在类加载的时候,instance静态实例就已经创建并初始化好了,所以,instance实例的创建过程是线程安全的。
var cfg *config
func init() {
cfg = &config{
Name: "我被初始化了",
}
}
type config struct {
Name string
}
// NewConfig 提供获取实例的方法
func NewConfig() *config {
return cfg
}
这种方式就是资源提前初始化,有人会讲需要的时候才去初始化,能够避免资源的浪费,不过也有不同的看法。
1、如果初始化耗时长,那我们最好不要等到真正要用它的时候,才去执行这个耗时长的初始化过程,这会影响到系统的性能(比如,在响应客户端接口请求的时候,做这个初始化操作,会导致此请求的响应时间变长,甚至超时)。采用饿汉式实现方式,将耗时的初始化操作,提前到程序启动的时候完成,这样就能避免在程序运行的时候,再去初始化导致的性能问题。
2、如果实例占用资源多,按照fail-fast的设计原则(有问题及早暴露),那我们也希望在程序启动时就将这个实例初始化好。如果资源不够,就会在程序启动的时候触发报错(比如Java中的 PermGen Space OOM),我们可以立即去修复。这样也能避免在程序运行一段时间后,突然因为初始化这个实例占用资源过多,导致系统崩溃,影响系统的可用性。
双重检测
饿汉式不支持延迟加载,懒汉式有性能问题,不支持高并发。这里又引入了一种双重检测的方法。
在这种实现方式中,只要instance被创建之后,即便再调用getInstance()函数也不会再进入到加锁逻辑中了。
来看下代码的实现
// 使用结构体代替类
type Tool struct {
Name string
}
//锁对象
var lock sync.Mutex
var instance *Tool
//第一次判断不加锁,第二次加锁保证线程安全,一旦对象建立后,获取对象就不用加锁了。
func GetInstance() *Tool {
if instance == nil {
lock.Lock()
if instance == nil {
instance = &Tool{
Name: "我是双重检测,我已经初始化了",
}
}
lock.Unlock()
}
return instance
}
sync.Once
go 中也提供了 sync.Once 这个方法,来控制只执行一次,具体源码参见go中sync.Once源码解读
// 使用结构体代替类
type Tool struct {
Name string
}
var instance *Tool
var once sync.Once
func GetOnceInstance() *Tool {
once.Do(func() {
instance = &Tool{
Name: "我sync.once初始化的,我已经初始化了",
}
})
return instance
}
参考
【单例模式】https://zh.wikipedia.org/wiki/单例模式
【大话设计模式】https://book.douban.com/subject/2334288/
【极客时间】https://time.geekbang.org/column/intro/100039001
【单例模式的优缺点和使用场景】https://www.cnblogs.com/damsoft/p/6105122.html
【单例模式】https://boilingfrog.github.io/2021/11/04/使用go实现单例模式/
设计模式学习-使用go实现单例模式的更多相关文章
- C#设计模式学习笔记:(1)单例模式
本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/8250985.html,记录一下学习过程以备后续查用. 一.引言 设计模式的分类: 1)依目的: 创建型(Cr ...
- 设计模式学习笔记c++版——单例模式
特别注意单例模式c++实现在main.cpp中引用的时候要去申明下: Singleton * Singleton::m_Instance = NULL; //定义性声明 不然会报错:无法解析的外部符号 ...
- C#设计模式学习笔记-单例模式随笔
最近学习 设计模式,从单例模式入手 啥是单例模式: 要实现一个单例类的话,首先,肯定是不能让用户自行生产的,那就是说明不能让用户new,所以,就必须把构造函数设置成为私有的 因为静态变量的生命周期跟整 ...
- C#设计模式学习笔记-单例模式(转)
C#设计模式学习笔记-单例模式 http://www.cnblogs.com/xun126/archive/2011/03/09/1970807.html 最近在学设计模式,学到创建型模式的时候,碰到 ...
- Java设计模式学习笔记(五) 单例模式
前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 1. 使用单例模式的原因 以Windows任务管理器为例,在Windows系统中,任务管理器是唯 ...
- Design Patterns Simplified - Part 2 (Singleton)【设计模式简述--第二部分(单例模式)】
原文链接: http://www.c-sharpcorner.com/UploadFile/19b1bd/design-patterns-simplified-part-2-singleton/ De ...
- Java设计模式学习资源汇总
本文记录了Java设计模式学习书籍.教程资源.此分享会持续更新: 1. 设计模式书籍 在豆瓣上搜索了一把,发现设计模式贯穿了人类生活的方方面面.还是回到Java与程序设计来吧. 打算先归类,再浏览,从 ...
- python之路,Day24 常用设计模式学习
python之路,Day24 常用设计模式学习 本节内容 设计模式介绍 设计模式分类 设计模式6大原则 1.设计模式介绍 设计模式(Design Patterns) --可复用面向对象软件的基础 ...
- Java设计模式学习总结
设计模式基础学习总结 这篇总结主要是基于我之前设计模式基础系列文章而形成的的.主要是把重要的知识点用自己的话说了一遍,可能会有一些错误,还望见谅和指点.谢谢 更多详细内容可以查看我的专栏文章:设计模式 ...
随机推荐
- Java基础系列(7)- 标识符和关键字
关键字 标识符 Java所有的组成部分都需要名字.类名.变量名.方法名都称为标识符 首字母以字母(A-Z或者a-z),美元符号($),或者下划线(_)开头 首字母之后可以用字母.美元符号.下划线.数字 ...
- Shell系列(15)- 数值运算方法
数值运算-方法1 declare -i [root@localhost ~]# aa=11 [root@localhost ~]# aa=22 #给变量aa和bb赋值 [root@localhos ...
- Jmeter目录分析
通讯协议 最重要的基础知识. 性能测试的原理?通过协议模拟压力 jmeter目录结果解析 bin docs,文档 lib,存放库文件,所依赖的jar包 ext jmeter自己的jar包 jmeter ...
- Oracle基本入门
一.数据的存储 1.java 程序中的对象:数组.集合保存.当运行的程序结束的时候,里面的数据就消亡. 2.文件存储系统: 存在的缺陷: 2.1)没有明确的数据类型划分. 2.2)没有用户身份验证机制 ...
- [转载20131024]Nginx服务器漏洞的利用和修复方法
本文主要分为两大部分,第一部分介绍了Nginx的一些常见安全漏洞的形成原因.利用方法,并给出了相应的解决办法;第二部分介绍了Nginx安全加固时需要关注的主要内容. Nginx(发音同engine x ...
- HTML 网页开发、CSS 基础语法——六. HTML基本结构
1.基本骨架 HTML文件最基本的四个标签,组成了网页的基本骨架,包括:<html>. <head>.<title>.<body>四组标签. ① < ...
- Ubuntu解决安装没有候选
Ubuntu解决安装没有候选 很多初次上手的小白们最头疼的可能就是一下这种问题了 这是我也载过很多次的坑,原因是软件安装源的问题,需要去软件安装设置里更改合适的源 结局方案如下:(具体操作) 有个So ...
- Bootstrap的模态框无法弹出的问题
今天在使用Bootstrap官网所提供的模态框插件时候发现其中的 可选尺寸模态框 无法弹出 在模态框前使用过其他 Bootstrap的js插件,可以正常使用,说明所需依赖js文件已经正常引用 注意:j ...
- css的公共属性及原因
在我们写多个网页的时候,会发现总会遇到很多相同的css样式,若是每次都要在网页代码中写,会浪费时间,同时也会消耗浏览器和计算机的性能.因此,我个人将我敲代码过程中的经常用到的css样式总结了一下.再用 ...
- 10.11 HTTPS
没有HTTPS的抓包截图 HTTPS=HTTP + TLS/SSL https 实现过程如下 1.客户端发起HTTPS请求 rewrite www.baidu.com https://www.baid ...