IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> [设计模式 in Golang]单例模式 -> 正文阅读

[Java知识库][设计模式 in Golang]单例模式

前言

单例模式是最简单的一个模式,指的是全局只有一个实例,并且它负责创建自己的对象。

单例模式不仅有利于减少内存开支,还有减少系统性能开销、防止多个实例产生冲突等优点。
因为单例模式保证了实例的全局唯一性,而且只被初始化一次,所以比较适合全局共享一个实例,且只需要被初始化一次的场景,例如数据库实例、全局配置、全局任务池等。

两个方式

单例模式又分为饿汉方式和懒汉方式。

饿汉方式

饿汉方式指全局的单例实例在包被加载时创建,

go中有两种常见写法,一种是利用全局变量的初始化:

package singleton

type singleton struct {
}

var instance = &singleton{}

func getInstance() *singleton {
	return instance
}

或者可以通过使用init函数,init函数会在package被加载时执行一次,从而避开了并发:

package singleton

type singleton struct {
}

var instance *singleton

func getInstance() *singleton {
	return instance
}

func init() {
	instance = &singleton{}
}

我们可以写个测试验证确实getInstance返回的是同一个实例:

package singleton

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func Test_getInstance(t *testing.T) {
	assert.Equal(t, getInstance(), getInstance())
	assert.Equal(t, getInstance(), instance)
}
$ go test -v
=== RUN   Test_getInstance
--- PASS: Test_getInstance (0.00s)
PASS
ok      ……/demo      1.660s

饿汉模式没有线程安全性问题,不需要考虑懒加载,获取实例的效率特高。缺点是如果初始化比较耗时,会导致程序加载时间比较长;即使没有用到这个对象,只要import路径里有它也照样要进行初始化。

PS:业务中看到有的人会在初始化代码中写一些外部依赖的代码,比如ping个网络地址,甚至还写个如果失败的话就panic,这就给本地测试等带来了很大的麻烦。这种饿汉模式简直是灾难。

懒汉模式

现在流行懒汉模式“延迟加载”,换句话说,在第一次使用这个单例类的时候才实际创建并初始化这个实例。

一个最简单的懒汉模式示例代码如下:

package singleton

type singleton struct {
}

var instance *singleton

func getInstance() *singleton {
	if instance == nil {
		instance = &singleton{}
	}
	return instance
}
package singleton

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func Test_getInstance(t *testing.T) {
	assert.Nil(t, instance)
	assert.Equal(t, getInstance(), getInstance())
	assert.NotNil(t, instance)
	assert.Equal(t, getInstance(), instance)
}

这个代码在单线程的环境下工作良好,但是在多线程环境下会有并发问题,如果两个线程同时getInstance,可能会创建出两个实例。

经典的线程安全的懒汉模式go语言实现示例如下:

package singleton

import "sync"

type singleton struct {
}

var (
	instance *singleton
	lock     sync.Mutex
)

func getInstance() *singleton {
	if instance == nil {
		lock.Lock()
		if instance == nil {
			instance = &singleton{}
		}
		lock.Unlock()
	}
	return instance
}

当发现instance为nil后,会加锁准备创建对象,加锁成功后必须重新检查是否为nil,应为可能被并发的另一个线程抢先创建了。

在go语言中,我们还可以使用Once实现只初始化一次(看着好像更高端了):

package singleton

import "sync"

type singleton struct {
}

var (
	instance *singleton
	once     sync.Once
)

func getInstance() *singleton {
	once.Do(func() {
		instance = &singleton{}
	})
	return instance
}

Once可以保证只执行一次,且在执行时阻塞同时想要执行的其他线程。

肯定有小可爱觉得这样的写法更好:

func getInstance() *singleton {
	if instance == nil {
		once.Do(func() {
			instance = &singleton{}
		})
	}
	return instance
}

但是实际上,通过基准测试发现,两种写法的性能没啥差。

结语

好像没什么好写的,这是最简单的设计模式,就算没学过设计模式也几乎在实际工作中会使用到。了解一下即可。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-09-24 10:24:23  更:2021-09-24 10:25:25 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 19:08:21-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码