單例模式在開發中是一種相對簡單的設計模式,但它在實現上又有很多種方式
熟悉java的同學知道在java中實現單例常見的有懶漢式、餓漢式、雙重檢查、內部靜態類、枚舉單例等(傳送門)
而由於語言的特性,golang目前常見的有以下四種方式(懶漢式、餓漢式、雙重檢查、sync.Once)
1.懶漢式----非線程安全
非線程安全,即在多線程下可能會創建多次對象
/** * 使用結構體代替類 */ type Tool struct { values int } /** * 建立私有變量 */ var instance *Tool /** * 獲取單例對象的方法,引用傳遞返回 */ func GetInstance() *Tool { if instance == nil { instance = new(Tool) } return instance }
2.懶漢式----線程安全
在非線程安全的基本上,利用Sync.Mutex進行加鎖,保證線程安全,但由於每次調用該方法都進行了加鎖操作,在性能上相對不高效
/** * 鎖對象 */ var lock sync.Mutex /** * 加鎖保證線程安全 */ func GetInstance() *Tool { lock.Lock() defer lock.Unlock() if instance == nil { instance = new(Tool) } return instance }
3.餓漢式
直接創建好對象,這樣不需要判斷為空,同時也是線程安全。唯一的缺點是在導入包的同時會創建該對象,並持續占有在內存中。
var instance Tool func GetInstance() *Tool { return &instance }
4.雙重檢查
在懶漢式(線程安全)的基礎上再進行憂化,判少加鎖的操作。保證線程安全同時不影響性能
/** * 鎖對象 */ var lock sync.Mutex /** * 第一次判斷不加鎖,第二次加鎖保證線程安全,一旦對象建立后,獲取對象就不用加鎖了 */ func GetInstance() *Tool { if instance == nil { lock.Lock() if instance == nil { instance = new(Tool) } lock.Unlock() } return instance }
5.sync.Once
通過sync.Once 來確保創建對象的方法只執行一次
var once sync.Once func GetInstance() *Tool { once.Do(func() { instance = new(Tool) }) return instance }
sync.Once內部本質上也是雙重檢查的方式,但在寫法上會比自己寫雙重檢查更簡潔,以下是Once的源碼
func (o *Once) Do(f func()) {
//判斷是否執行過該方法,如果執行過則不執行 if atomic.LoadUint32(&o.done) == 1 { return } // Slow-path. o.m.Lock() defer o.m.Unlock()
//進行加鎖,再做一次判斷,如果沒有執行,則進行標志已經掃行並調用該方法 if o.done == 0 { defer atomic.StoreUint32(&o.done, 1) f() } }