Loading

Golang与设计模式

单例模式

因为之前研究Java比较多,所以当我试着使用go来实现一些设计模式的时候,首先想到的就是照搬Java中的思路,后面对go了解加深之后又增加了一些新的思路。
在Java中实现的单例模式的思路有很多,但是比较好的两个思路是利用类加载机制生成单例对象check-lock-check机制避免并发问题
我们给出这两个思路的Java代码,

  1. 利用类加载机制初始化类的静态变量,在类加载过程中的初始化阶段,会执行程序员编写的赋值语句和静态代码块,生成一个单例对象,并且这个生成过程只会执行一次,保证只生成一个单例对象。

    public class Singleton {
    	private static Singleton instance = new Singleton();
    	private Singleton (){}
    	public static Singleton getInstance() {
    		return instance;
    	}
    }
    
  2. check-lock-check机制,又被称为双检锁机制,第一次检查,如果已经生成了单例,就直接返回这个对象,如果还未生成单例对象,先上锁,避免多个线程去创建单例对象,再一次检查,是因为当有多个线程进入了第一次检查,我们只允许第一个进入锁的线程创建对象,其他线程都不要再创建对象。(此外volatile是为了让创建对象过程中严格遵守:分配内存,初始化,赋值引用的顺序,这里不再详细介绍,具体内容见另一篇文章,Java与设计模式)

    public class Singleton {
    	private volatile static Singleton singleton;
    	private Singleton (){}
    	public static Singleton getSingleton() {
    		if (singleton == null) {
    			synchronized (Singleton.class) {
    				if (singleton == null) {
    					singleton = new Singleton();
    				}
    			}
    		}
    		return singleton;
    	}
    }
    

我们可以直接照猫画虎吗?显然第一种方法是不可以,没有办法利用类加载机制。
来看第二种方法,我们使用了Mutex进行了加锁操作。

type singleton struct{}
var instance *singleton
var mu sync.Mutex
func GetSingleton() *singleton {
    if instance == nil {
        mu.Lock()
        defer mu.Unlock()
        if instance == nil {
            instance = &singleton{}
        }
    }
    return instance
}

此外,Go 语言在 sync 包中提供了 Once 机制能够帮助我们写出更加优雅的代码,Once 是一个结构体,在执行 Do 方法的内部通过 atomic 操作和加锁机制来保证并发安全,且 once.Do 能够保证多个 goroutine 同时执行时 &singleton{} 只被创建一次

首先让我们来看看Once,

// Once is an object that will perform exactly one action.
type Once struct {
	// done indicates whether the action has been performed.
	// It is first in the struct because it is used in the hot path.
	// The hot path is inlined at every call site.
	// Placing done first allows more compact instructions on some architectures (amd64/x86),
	// and fewer instructions (to calculate offset) on other architectures.
	done uint32
	m    Mutex
}

func (o *Once) Do(f func()) {
	if atomic.LoadUint32(&o.done) == 0 { // check
		// Outlined slow-path to allow inlining of the fast-path.
		o.doSlow(f)
	}
}

func (o *Once) doSlow(f func()) {
	o.m.Lock()                          // lock
	defer o.m.Unlock()
	
	if o.done == 0 {                    // check
		defer atomic.StoreUint32(&o.done, 1)
		f()
	}
}

这说明我们可以借助这个实现只执行一次某个函数/方法,once.Do()的用法如下:

once.Do(func() {
    // 在这里执行安全的初始化
})

发现了吗?这个Once其实也是一个check-lock-check模式
现在我们可以利用这个特性来写单例模式了,

package singleton
import "sync"
type singleton struct{}
var instance *singleton
var once sync.Once
func GetSingleton() *singleton {
    once.Do(func() {
        instance = &singleton{}
    })
    return instance
}
posted @   Duancf  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
点击右上角即可分享
微信分享提示