转: Go -- 单例
发布日期:2021-08-23 22:52:29 浏览次数:1 分类:技术文章

本文共 1836 字,大约阅读时间需要 6 分钟。

最近在学习Golang,想着可以就以前的知识做一些串通,加上了解到go语言也是面向对象编程语言之后。在最近的开发过程中,我碰到一个问题,要用go语言实现单例模式。本着“天下知识,同根同源”(我瞎掰的~),我心想,这有什么难的,可是真正做起来,还是碰到了不少问题。

  下面是我的经历:

  1.我先是完成了我的第一版单例模式,就是非并发,最简单的一种,懒汉模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var 
instance *single
type 
single 
struct
{
    
Name string
}
func 
GetInstance()*single{
    
if 
m == nil{
        
m = &single{}
    
}
    
return 
m
}
func 
main(){
    
a := GetInstance()
    
a.Name = 
"a"
    
b := GetInstance()
    
b.Name = 
"b"
    
fmt.Println(&a.Name, a)
    
fmt.Println(&b.Name, b)
    
fmt.Printf(
"%p %T\n"
, a, a)
    
fmt.Printf(
"%p %T\n"
, b, b)
}

  结果如下:

0xc04203e1b0 &{b}

0xc04203e1b0 &{b}
0xc04203e1b0 *main.single
0xc04203e1b0 *main.single

  可以看到,我们已经实现了简单的单例模式,我们申请了两次实例,在改变一个第二个实例的字段之后,第一个也随之改变了。而且从他们的地址都相同也可以看出是同一个对象。但是,这样简陋的单例模式在并发下就容易出错,非线程安全的。

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

  2.紧接着我们做了一些改进,给单例模式加了锁:

1
2
3
4
5
6
7
8
9
10
11
12
13
var 
m *single
var 
lock sync.Mutex
type 
single 
struct
{
    
Name string
}
func 
GetInstance()*single{
    
lock.Lock()
    
defer 
lock.Unlock()
    
if 
m == nil{
        
m = &single{}
    
}
    
return 
m
}

  结果同上。

  与此同时,新的问题出现了,在高并发环境下,现在不管什么情况下都会上一把锁,而且加锁的代价是很大的,有没有办法继续对我们的代码进行进一步的优化呢?

  3.双重锁机制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var 
m *single
var 
lock sync.Mutex
type 
single 
struct
{
    
Name string
}
func 
GetInstance()*single{
    
if 
m == nil{
        
lock.Lock()
        
defer 
lock.Unlock()
        
if 
m == nil{
            
m = &single{}
        
}
    
}
    
return 
m
}

  这次我们用了两个判断,而且我们将同步锁放在了条件判断之后,这样做就避免了每次调用都加锁,提高了代码的执行效率。理论上写到这里已经是很完美的单例模式了,但是我们在go语言里,我们有一个很优雅的写法。

  4.sync包里的Once.Do()方法

1
2
3
4
5
6
7
8
9
10
11
12
var 
m *single
var 
once sync.Once
 
type 
single 
struct
{
    
Name string
}
func 
GetInstance()*single{
    
once.Do(
func
() {
        
m = &single{}
    
})
    
return 
m
}

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

转载地址:https://blog.csdn.net/weixin_33804582/article/details/86028042 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Docker入门学习
下一篇:spring boot: 支持jsp,支持freemarker

发表评论

最新留言

逛到本站,mark一下
[***.202.152.39]2024年04月16日 20时31分33秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章