go解鎖設計模式之單例模式(有個小坑要注意一下啊)

前言

哈嘍,你們好,我是asong,這是個人第16篇原創文章,感謝各位的關注。今天給你們分享設計模式之單例模式,並使用go語言實現。熟悉java的同窗對單例模式必定不陌生,單例模式,是一種很常見的軟件設計模式,在他的核心結構中只包含一個被稱爲單例的特殊類。經過單例模式能夠保證系統中一個類只有一個實例且該實例易於外界訪問,從而方便對實例個數的控制並節約系統資源。下面咱們就一塊兒來看一看怎麼使用go實現單例模式,這裏有一個小坑,必定要注意一下,結尾告訴你哦~~~

什麼是單例模式

單例模式確保某一個類只有一個實例。爲何要確保一個類只有一個實例?有何時才須要用到單例模式呢?聽起來一個類只有一個實例好像沒什麼用呢! 那咱們來舉個例子。好比咱們的APP中有一個類用來保存運行時全局的一些狀態信息,若是這個類實現不是單例的,那麼App裏面的組件可以隨意的生成多個類用來保存本身的狀態,等於你們各玩各的,那這個全局的狀態信息就成了笑話了。而若是把這個類實現成單例的,那麼無論App的哪一個組件獲取到的都是同一個對象(好比Application類,除了多進程的狀況下)。java

image

餓漢模式

這裏咱們使用三種方式實現餓漢模式。先說一下什麼是懶漢模式吧,從懶漢這兩個字,咱們就能知道,這我的很懶,因此他不可能在未使用實例時就建立了對象,他確定會在使用時纔會建立實例,這個好處的就在於,只有在使用的時候纔會建立該實例。下面咱們一塊兒來看看他的實現:git

  • 不加鎖
package one

type singleton struct {

}

var  instance *singleton
func GetInstance() *singleton {
    if instance == nil{
        instance = new(singleton)
    }
    return instance
}

這種方法是會存在線程安全問題的,在高併發的時候會有多個線程同時掉這個方法,那麼都會檢測instance爲nil,這樣就會致使建立多個對象,因此這種方法是不推薦的,我再來看第二種寫法。github

  • 整個方法加鎖
type singleton struct {

}

var instance *singleton
var lock sync.Mutex

func GetInstance() *singleton {
    lock.Lock()
    defer lock.Unlock()
    if instance == nil{
        instance = new(singleton)
    }
    return instance
}

這裏對整個方法進行了加鎖,這種能夠解決併發安全的問題,可是效率就會降下來,每個對象建立時都是進行加鎖解鎖,這樣就拖慢了速度,因此不推薦這種寫法。golang

  • 建立方法時進行鎖定
type singleton struct {

}

var instance *singleton
var lock sync.Mutex

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

這種方法也是線程不安全的,雖然咱們加了鎖,多個線程一樣會致使建立多個實例,因此這種方式也不是推薦的。因此就有了下面的雙重檢索機制面試

  • 雙重檢鎖
type singleton struct {
    
}

var instance *singleton
var lock sync.Mutex

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

這裏在上面的代碼作了改進,只有當對象未初始化的時候,纔會有加鎖和減鎖的操做。可是又出現了另外一個問題:每一次訪問都要檢查兩次,爲了解決這個問題,咱們可使用golang標準包中的方法進行原子性操做。設計模式

  • 原子操做實現
type singleton struct {
    
}

var instance *singleton
var once sync.Once
func GetInstance() *singleton {
    once.Do(func() {
        instance = new(singleton)
    })
    return instance
}

這裏使用了sync.OnceDo方法能夠實如今程序運行過程當中只運行一次其中的回調,這樣就能夠只建立了一個對象,這種方法是推薦的~~~。安全

餓漢模式

有懶漢模式,固然還要有餓漢模式啦,看了懶漢的模式,餓漢模式咱們很好解釋了,由於他餓呀,因此很着急的就建立了實例,不用等到使用時才建立,這樣咱們每次調用獲取接口將不會從新建立新的對象,而是直接返回以前建立的對象。比較適用於:若是某個單例使用的次數少,而且建立單例消息的資源比較多,那麼就須要實現單例的按需建立,這個時候懶漢模式就是一個不錯的選擇。不過也有缺點,餓漢模式將在包加載的時候就會建立單例對象,當程序中用不到該對象時,浪費了一部分空間,可是相對於懶漢模式,不須要進行了加鎖操做,會更安全,可是會減慢啓動速度。架構

下面咱們一塊兒來看看go實現餓漢模式:併發

type singleton struct {

}

var instance = new(singleton)

func GetInstance()  *singleton{
    return instance
}

或者
type singleton struct {

}

var instance *singleton

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

func GetInstance()  *singleton{
    return instance
}

這兩種方法均可以,第一種咱們採用建立一個全局變量的方式來實現,第二種咱們使用init包加載的時候建立實例,這裏兩個均可以,不過根據golang的執行順序,全局變量的初始化函數會比包的init函數先執行,沒有特別的差距。框架

小坑

還記得我開頭說的一句話,go語言中使用單例模式有一個小坑,若是不注意,就會致使咱們的單例模式沒有用,能夠觀察一下我寫的代碼,除了GetInstance方法外其餘都使用的小寫字母開頭,知道這是爲何嗎?

golang中根據首字母的大小寫來肯定能夠訪問的權限。不管是方法名、常量、變量名仍是結構體的名稱,若是首字母大寫,則能夠被其餘的包訪問;若是首字母小寫,則只能在本包中使用。能夠簡單的理解成,首字母大寫是公有的,首字母小寫是私有的。這裏type singleton struct {咱們若是使用大寫,那麼咱們寫的這些方法就沒有意義了,其餘包能夠經過s := &singleton{}建立多個實例,單例模式就顯得很沒有意義了,因此這裏必定要注意一下哦~~~

總結

這一篇就到此結束了,這裏講解了23種模式中最簡單的單例模式,雖然他很簡單,可是越簡單的越容易犯錯的呦,因此必定要細心對待每一件事情的呦~~

好啦,這一篇就到此結束了,個人代碼已上傳github:https://github.com/asong2020/...

歡迎star

結尾給你們發一個小福利吧,最近我在看[微服務架構設計模式]這一本書,講的很好,本身也收集了一本PDF,有須要的小夥能夠到自行下載。獲取方式:關注公衆號:[Golang夢工廠],後臺回覆:[微服務],便可獲取。

我翻譯了一份GIN中文文檔,會按期進行維護,有須要的小夥伴後臺回覆[gin]便可下載。

我是asong,一名普普統統的程序猿,讓我一塊兒慢慢變強吧。我本身建了一個golang交流羣,有須要的小夥伴加我vx,我拉你入羣。歡迎各位的關注,咱們下期見~~~

推薦往期文章:

相關文章
相關標籤/搜索