文档章节

GO 单例

叫我亮仔
 叫我亮仔
发布于 2017/08/22 10:53
字数 1375
阅读 20
收藏 0

这篇博客我们继续来看设计模式,今天带来的是一个最简单而且最常用的模式-单例模式。那什么是单例模式呢?相信大家最它最熟悉不过了,那我们就来快速的了解一下它的定义。

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

这个解释足够简单。说白了就是假如我们希望我们在我们的系统中该类仅仅存在1个或0个该类的实例。虽然单例模式很简单,但是熟悉Java的同学可能了解,单例模式有很多写法,懒汉式饿汉式双重锁。。。 这么多形式,难道有什么目的?确实,不过他们的目的很明确,就是保证在一种特殊情况下的单例-并发

ok,既然了解了单例模式,那下面我们就开始用代码描述一下单例模式。首先是最简单的单例,这里我们并不去考虑并发的情况

package manager
import (
    "fmt"
)

var m *Manager

func GetInstance() *Manager {
    if m == nil {
        m = &Manager {}
    }
    return m
}

type Manager struct {}

func (p Manager) Manage() {
    fmt.Println("manage...")
}

这就是一个最简单的单例了,对于Manager结构体,我们提供了一个GetInstance函数去获取它的实例,这个函数中首先去判断m变量是否为空,如果为空才去赋值一个Manager的指针类型的值,一个小小的判断,就保证了我们在第第二次调用GetInstance的时候直接返回m,而不是重新获取Manager的实例,进而保证了唯一实例。

上面的代码确实简单,也实现了最简单的单例模式,不过大家有没有考虑到并发这一点,在并发的情况下,这里是不是还可以正常工作呢? 来,先跟着下面的思路走一走,来看看问题出现在哪。

现在我们是在并发的情况下去调用的 GetInstance函数,现在恰好第一个goroutine执行到m = &Manager {}这句话之前,第二个goroutine也来获取实例了,第二个goroutine去判断m是不是nil,因为m = &Manager{}还没有来得及执行,所以m肯定是nil,现在出现的问题就是if中的语句可能会执行两遍!

在上面介绍的这种情形中,因为m = &Manager{}可能会执行多次,所以我们写的单例失效了,这个时候我们就该考虑为我们的单例加锁啦。

这个时候我们就需要引入Go的锁机制-sync.Mutex了,修改我们的代码,

package manager
import (
    "sync"
    "fmt"
)

var m *Manager
var lock *sync.Mutex = &sync.Mutex {}

func GetInstance() *Manager {
    lock.Lock()
    defer lock.Unlock()
    if m == nil {
        m = &Manager {}
    }
    return m
}

type Manager struct {}

func (p Manager) Manage() {
    fmt.Println("manage...")
}

代码做了简单的修改了,引入了锁的机制,在GetInstance函数中,每次调用我们都会上一把锁,保证只有一个goroutine执行它,这个时候并发的问题就解决了。不过现在不管什么情况下都会上一把锁,而且加锁的代价是很大的,有没有办法继续对我们的代码进行进一步的优化呢? 熟悉java的同学可能早就想到了双重的概念,没错,在go中我们也可以使用双重锁机制来提高效率。

package manager
import (
    "sync"
    "fmt"
)

var m *Manager
var lock *sync.Mutex = &sync.Mutex {}

func GetInstance() *Manager {
    if m == nil {
        lock.Lock()
        defer lock.Unlock()
        if m == nil {
            m = &Manager {}
        }
    }

    return m
}

type Manager struct {}

func (p Manager) Manage() {
    fmt.Println("manage...")
}

代码只是稍作修改而已,不过我们用了两个判断,而且我们将同步锁放在了条件判断之后,这样做就避免了每次调用都加锁,提高了代码的执行效率。

这获取就是很完美的单例代码了,不过还没完,在go中我们还有更优雅的方式去实现。单例的目的是啥?保证实例化的代码只执行一次,在go中就中这么一种机制来保证代码只执行一次,而且不需要我们手工去加锁解锁。对,就是我们的sync.Once,它有一个Do方法,在它中的函数go会只保证仅仅调用一次!再次修改我们的代码,

package manager
import (
    "sync"
    "fmt"
)

var m *Manager
var once sync.Once

func GetInstance() *Manager {
    once.Do(func() {
        m = &Manager {}
    })
    return m
}

type Manager struct {}

func (p Manager) Manage() {
    fmt.Println("manage...")
}

代码更简单了,而且有没有发现-漂亮了!Once.Do方法的参数是一个函数,这里我们给的是一个匿名函数,在这个函数中我们做的工作很简单,就是去赋值m变量,而且go能保证这个函数中的代码仅仅执行一次!

ok,到现在单例模式我们就介绍完了,内容并不多,因为单例模式太简单而且太常见了。我们用单例的目的是为了保证在整个系统中存在唯一的实例,我们加锁的目的是为了在并发的环境中单例依旧好用。不过虽然单例简单,我们还是不能任性的用,因为这样做实例会一直存在内存中,一些我们用的不是那么频繁的东西使用了单例是不是就造成了内存的浪费?大家在用单例的时候还是要多思考思考,这个模块适不适合用单例!

本文转载自:http://blog.csdn.net/qibin0506/article/details/50733314

上一篇: GO 随机数
叫我亮仔
粉丝 0
博文 17
码字总数 888
作品 0
广州
私信 提问
单例模式Singleton

单例模式定义 确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。 单例模式类图 类图说明 Client:高层客户类 Singleton:单例类 实现单例模式的要点 构造函数不对外开放,一...

Gnepux
2016/11/28
9
0
【单例设计模式】单例模式中为什么用枚举更好

枚举单例(Enum Singleton)是实现单例模式的一种新方式,尽管单例模式在java中已经存在很长时间了,但是枚举单例相对来说是一种比较新的概念,枚举这个特性是在Java5才出现的,这篇文章主要...

冷冷gg
2016/08/30
147
0
(4)单例模式

一:单例模式的优点   --->单例类只能有一个实例 --->单例类必须自己创建自己的唯一实例。 --->单例类必须给所有其他对象提供这一实例。 二:单例模式分类   --->饿汉模式 --->懒汉模式 ...

无信不立
2015/07/12
0
0
你敢说自己了解单例模式?

一、背景   最近在学习设计模式,在看到单例模式的时候,我一开始以为直接很了解单例模式了,实现起来也很简单,但是实际上单例模式有着好几个变种,并且多线程中涉及到线程安全问题,那么...

hafiz.zhang
2017/09/27
0
0
单例设计模式 (2)

1.静态内部类实现 在上一版的时候用的是懒汉和饿汉来做单例模式,如果我们采用静态内部类的话,就可以通过classloader来懒加载单例 用静态内部类实现单例模式: 这里有几个需要注意的点: 1....

蠢廿
2017/12/04
0
0

没有更多内容

加载失败,请刷新页面

加载更多

视频如何加水印?

很多视频制作者的视频都被他人盗用过,为了防止自己的劳动成果被他人窃取,给视频加水印对于视频制作者来说,是一件非常重要的事情。那么下面分享一个手机给视频加水印的方法,一起来看看吧!...

白米稀饭2019
33分钟前
5
0
004-Envelop-基于Blockstack的文件传输dapp

本篇文章主要介绍基于Blockstack的文件传输工具; ####A-链接地址 官网地址:https://envelop.app/ Github地址:https://github.com/envelop-app ####B-特性: 1: Share private files easil...

Riverzhou
36分钟前
7
0
SpringCloud——声明式调用Feign

Feign声明式调用 一、Feign简介 使用Ribbon和RestTemplate消费服务的时候,有一个最麻烦的点在于,每次都要拼接URL,组织参数,所以有了Feign声明式调用,Feign的首要目标是将Java HTTP客户端...

devils_os
42分钟前
7
0
《JAVA核心知识》学习笔记 (22. 数据结构)

22.1.1. 栈(stack) 栈( stack)是限制插入和删除只能在一个位置上进行的表,该位置是表的末端,叫做栈顶 (top)。它是后进先出(LIFO)的。对栈的基本操作只有 push(进栈)和 pop(出栈...

Shingfi
47分钟前
6
0
你对AJAX认知有多少(1)?

AJAX(一) AJAX技术对于前段或者后端工程师来说,都是必不可缺的 那我们这几期都来细细品味一下AJAX的相关知识,直接上干货喽~ 1、什么是AJAX,为什么要使用Ajax(请谈一下你对Ajax的认识) 什么...

理性思考
55分钟前
15
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部