寫在前面
單例模式是最常用的設(shè)計模式之一,雖然簡單,但是還是有一些小坑點需要注意。本文介紹單例模式并使用go語言實現(xiàn)一遍單例模式。
單例模式介紹
簡介
單例模式保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。
使用場景:
- 當(dāng)類只能有一個實例而且可以從一個公開的眾所周知的訪問點訪問它。
- 當(dāng)這個唯一實例是通過子類化可擴展的,并且應(yīng)該無須更改單例代碼就能使用一個擴展的實例。
結(jié)構(gòu)圖
分類
單例里面分成了兩種模式,一種是餓漢式,一種是懶漢式。
餓漢式
餓漢模式下的單例,只在使用實例的時候就創(chuàng)建了對象。
代碼實例:
var req *Request
type Request struct {
}
func NewRequest() *Request {
if req == nil {
req = new(Request)
}
return req
}
這個代碼看起來沒什么問題,但是這段代碼是線程不安全
的。 因為如果有多個請求過來,就會同時創(chuàng)建多個對象,就會出現(xiàn)頻繁的創(chuàng)建和刪除對象,給gc造成很大的壓力。
當(dāng)然我們也可以加鎖,但是頻繁的加鎖,解鎖會嚴(yán)重影響系統(tǒng)的性能。
var req *Request
var lock sync.Mutex
type Request struct {
}
func NewRequest() *Request {
lock.Lock()
defer lock.Unlock()
if req == nil {
req = new(Request)
}
return req
}
每一個對象的創(chuàng)建,都需要加鎖解鎖,非常影響性能
懶漢式
懶漢式的單例模式可以解決我們餓漢式的缺點,并且是線程安全的,保證了我們對象全局唯一并且不會重復(fù)創(chuàng)建。
代碼如下:
var req *Request
var reqOnce sync.Once
type Request struct {
}
func NewRequest() *Request {
reqOnce.Do(func() {
req = &Request{}
})
return req
}
這里我們用到go語言的 sync.Once 包,而sync.Once的作用就是使得我們的函數(shù)只執(zhí)行一次。并且是并發(fā)安全的。
那為什么是并發(fā)安全的呢?接下來我們詳解一下 sync.Once
sync.Once
sync.once 內(nèi)部原理很簡單,Once的結(jié)構(gòu)題如下:
type Once struct {
// done變量用來標(biāo)識函數(shù)是否執(zhí)行完畢
done uint32
// m用來保證函數(shù)執(zhí)行期間,其他goroutine阻塞
m Mutex
}
這個Do方法是判斷有無執(zhí)行過這個該方法,并且是使用atomic.LoadUint32
的原子操作。
func (o *Once) Do(f func()) {
// 1.判斷函數(shù)是否執(zhí)行過,done=0說明沒執(zhí)行過
if atomic.LoadUint32(&o.done) == 0 {
o.doSlow(f)
}
}
-
如果沒執(zhí)行過就執(zhí)行這個方法,那么這里會有一個double check的工作,其實就是防止可能在上一次檢查到這一次加鎖加完的時間段間隙上有人執(zhí)行完了這個函數(shù)
-
確認(rèn)確實沒有執(zhí)行的時候,就執(zhí)行函數(shù),并進(jìn)行原子操作標(biāo)識,將 done 置為1。文章來源:http://www.zghlxwxcb.cn/news/detail-861684.html
func (o *Once) doSlow(f func()) {
// 1.加鎖
o.m.Lock()
// 5.函數(shù)返回后,釋放鎖
defer o.m.Unlock()
// 2.判斷是否執(zhí)行過
if o.done == 0 {
// 4.函數(shù)返回后,設(shè)置執(zhí)行標(biāo)識
defer atomic.StoreUint32(&o.done, 1)
// 3.具體執(zhí)行函數(shù)
f()
}
}
應(yīng)用場景舉例子
我們可以在 service 上定義某一個方法的結(jié)構(gòu)體函數(shù),并將可以根據(jù)這個結(jié)構(gòu)體函數(shù)進(jìn)行方法的擴展。
在controll層只需要調(diào)用這個函數(shù)方法即可GetUserSrv()
文章來源地址http://www.zghlxwxcb.cn/news/detail-861684.html
到了這里,關(guān)于【設(shè)計模式】單例模式|最常用的設(shè)計模式的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!