设计模式-单例

108 阅读2分钟

单例

实例的对象只有一个,并提供全局访问的方法。

饿汉式

go:加载go文件所在的包时候,就已经创建好实例了。

java:类加载的时候,就已经创建好了。

  • 优点

能够保证,全局唯一性,线程安全。

  • 缺点

不支持懒加载(有的时候,我没有真正的时候,但是这个时候就已经初始化了)

  • 代码
package singleon

type singleton struct { _ int } // 这里需要指定一个属性,要不然每次创建地址都是同一个

var instance *singleton

// 在该 包 被引用时候,init函数会被调用
func init(){
  instance = new(singleton)
}

// 提供一个全局访问的方法
func GetInstance()*Singleton {
  return instance
}

懒汉式

  • 优点

只有在真正时候的时候才会去,“初始化对象一次”

  • 缺点

0、非线程安全,

0、如果创建的对象很复杂,可能还有有延迟。

  • 代码
package singleton

type singleton struct { _ int}

var instance *singleton 

func GetInstance()*singleton{
  // 多个线程/协成操作,可能会创建多个实例,不安全	
  if instance == nil {
	  instance = new(singleton)
  }

  return instance 
}

懒汉式+双重检测

  • 优点

懒加载,线层安全

  • 缺点

0、如果创建的对象很复杂,可能还有有延迟。

0、因为加锁的机制,导致 创建对象产生竞争关系, 单例对象的实例也可能会延迟

package singleton

import "sync"

type singleton struct { _ int}

var instance *singleton

// 使用锁
var lock sync.Mutex

func GetInstance()*singleton{
	if instance == nil {
		lock.Lock()
		defer lock.UnLock()
		if instance == nil {
			instance = new(singleton)
		}else{ // 为什么要在这里 写一个 else,减少 defer 的延迟的时间
			return instance
		}
	}
	return instance 
}
  • 思考为什么 要加在 第二if 的位置呢?

假如你加到 第一个 if 的位置,第一个请求获取到锁了并实例化好了,第二请求也会去获取锁,然后判断,这样做会频繁的获取锁,造成一些不必要的开销。我们只需要在,单例对象实例化好了,优先在判断一下,该对象是否已经实例化了,没有实例化,则 加锁 实例化,否则直接返回。减少 加锁的 粒度,越小越好。

sync.Once实现

package singleton

import "sync"

type singleton struct { _ int}

var instance *singleton

var once sync.Once 

func GetInstance()*singleton{
	if instance == nil {
		once.Do(func(){
			instance = new(singleton)
		})
	}

	return instance 
}