单例模式【Go实现】

Go实现设计模式——单例模式

单例模式(Singleton Pattern)

在软件中,个别对象仅仅需要创建一次(实例化一次),例如线程池、日志对象、状态管理器等。如果程序运行期间出现了多个实例,往往会造成诸多问题,比如程序的行为异常、资源的浪费,创建对象所造成的性能影响等。简单的情况下,可以通过开发人员之间的约定来保证独一性,比如通过静态变量、全局变量等来达成目的。但是,在更复杂的情况下,我们应当采用单例模式来强制保证对象的独一性。

开发人员使用关键字(new)或语法来实例化一个对象,最终想要的都是一个对象,所以,我们应该分隔开“实例化/创建对象”和“获取对象”两步操作——通过设计屏蔽实例化的接口,提供用户新的获取对象的接口。

懒汉式

在面向对象的语言中,可以通过将构造方法声明为private来实现第一步,并提供新的方法来返回对象。

Java 实现如下:

class Singleton{
    private static Singleton instance;
    private Singleton(){
        // do something
    }
    public Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

Go语言不是严格的面向对象的语言,没有构造方法,通过大小写来控制外部可见性。

type singleton struct{
    // some value
}

var singletonObj *singleton
func GetInstance() *singleton{
    if singletonObj == nil{
        singletonObj = &singleton{
            // assign value
        }
    }
    return singletonObj
}

我们实现了一个非常简单的满足单例模式的例子,这种的实现被称之为懒汉式,因为对象在第一次被获取的时候才会创建。这还有改进的空间。

我们没有对整个过程添加额外的判断和保障,所以,我们的代码如果运行在多线程下,那么依旧可能出现对象的多次实例化。所以我们需要对整个过程进行加锁来保障线程安全。Java可以通过对方法添加synchronized关键字来保障线程安全。Go的实现如下:

var lock sync.Mutex
var singletonObj *singleton
func GetInstance() *singleton{
    lock.Lock()
    defer lock.Unlock()
    if singletonObj == nil{
        singletonObj = &singleton{
            // assign value
        }
    }
    return singletonObj
}

这种方式的优点是保证了线程安全性,缺点是在高频率调用时,会反复加锁释锁,对性能造成一定的影响

饿汉式

相较于懒汉式,饿汉式通过在一开始就创建对象的方式,来避免对对象进行判空的操作,从而避免了线程的不安全性(任何时候进入,对象都是相同状态)。Go的实现如下:

type singleton struct{
    // some value
}

// init global object (valid in package)
var singletonObj *singleton = &singleton{
            // assign value
} 

func GetInstance() *singleton{
    return singletonObj
}

饿汉式的优点在于保证了线程的安全性,缺点是对象从程序开始时就一直创建在内存中,持续占有内存

双重检查

基于线程安全的饿汉式,如果我们能够想办法减少加锁释锁的次数,就能既保证线程安全也能避免对象过早创建带来的内存占用。

反思我们加锁的原因,是因为创建对象的步骤应该是独占的,如果多个线程分别检测到对象不存在,那么就会造成对象的多次创建,单例模式失效。我们可以通过增加检查步骤来避免这个问题(相较于加锁释锁,一次比较所增加的性能可以忽略不记)。Go的实现如下

var lock sync.Mutex
var singletonObj *singleton
func GetInstance() *singleton{
    if singletonObj == nil{
        lock.Lock()
        if singletonObj == nil{
            singletonObj = &singleton{
                // assign value
            }
        }
        lock.Unlock()
    }
    return singletonObj
}

Java实现如下:

class Singleton{
    private volatile static Singleton instance;
    private Singleton(){
        // do something
    }
    public Singleton getInstance(){
        if(instance == null){
            synchronized(Singleton.class){
                if(instance==null){
                	instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

Sync.Once

Go 的Sync包提供了Once类型,使用其Do方法来保证作为参数传入的方法仅仅执行一次,且是线程安全。实现如下:

var once Sync.Once
var singletonObj *singleton
func GetInstance() *singleton{
    once.Do(func(){
        singletonObj = &singleton{
            // assign value
        }
    })
    return singleObj
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值