寫乙個高併發下面的單例模式 單例模式詳解

2021-10-14 06:28:08 字數 3319 閱讀 6593

保證整個系統中乙個類只有乙個物件的例項,實現這種功能的方式就叫單例模式。

1、單例模式節省公共資源

比如:大家都要喝水,但是沒必要每人家裡都打一口井是吧,通常的做法是整個村里打乙個井就夠了,大家都從這個井裡面打水喝。

對應到我們計算機裡面,像日誌管理、印表機、資料庫連線池、應用配置。

2、單例模式方便控制

就像日誌管理,如果多個人同時來寫日誌,你一筆我一筆那整個日誌檔案都亂七八糟,如果想要控制日誌的正確性,那麼必須要對關鍵的**進行上鎖,只能乙個乙個按照順序來寫,而單例模式只有乙個人來向日誌裡寫入資訊方便控制,避免了這種多人干擾的問題出現。

1.構造私有:

如果要保證乙個類不能多次被例項化,那麼我肯定要阻止物件被new 出來,所以需要把類的所有構造方法私有化。

2.以靜態方法返回例項

因為外界就不能通過new來獲得物件,所以我們要通過提供類的方法來讓外界獲取物件例項。

3.確保物件例項只有乙個

只對類進行一次例項化,以後都直接獲取第一次例項化的物件。

/**

* 單例模式案例

*/public class singleton

//以靜態方法返回例項

public static singleton getinstance()

}

這裡類的例項在類初始化的時候已經生成,不再進行第二次例項化了,而外界只能通過singlecase.getinstance()方法來獲取singlecase物件, 所以這樣就保證整個系統只能獲取乙個類的物件例項。

餓漢模式

餓漢模式的意思是,我先把物件(麵包)建立好,等我要用(吃)的直接直接來拿就行了。

public class singleton 

//其他人來拿的時候直接返回已建立好的物件

public static singleton getinstance()

}

我們上面的案例就是使用的餓漢模式。 這種模式是最簡單最省心的,不足的地方是容易造成資源上的浪費(比如:我事先把麵包都做好了,但是你並不一定吃,這樣容易造成資源的浪費)。

懶漢模式

因為餓漢模式可能會造成資源浪費的問題,所以就有了懶漢模式,

懶漢模式的意思是,我先不建立類的物件例項,等你需要的時候我再建立。

/**

* 單例模式案例

*/public class singleton

//獲取物件的時候再進行例項化

public static singleton getinstance()

}return singleton;}}

懶漢模式在併發情況下可能引起的問題懶漢模式解決了餓漢模式可能引起的資源浪費問題,因為這種模式只有在使用者要使用的時候才會例項化物件。但是這種模式在併發情況下會出現建立多個物件的情況。

因為可能出現外界多人同時訪問singlecase.getinstance()方法,這裡可能會出現因為併發問題導致類被例項化多次,所以懶漢模式需要加上鎖synchronized (singleton.class)來控制類只允許被例項化一次。

如果不加鎖併發的情況下會出現這種情況

加鎖後就不會出現多個執行緒同時執行相同**的情況,因為執行緒是按佇列的形式執行的,只有當前乙個執行緒執行完之後才能進入**塊。

懶漢模式加鎖引起的效能問題

在上面的案例中,我們通過鎖的方式保證了單例模式的安全性,因為獲取物件的方法加鎖,多人同時訪問只能排隊等上乙個人執行完才能繼續執行,但加鎖的方式會嚴重影響效能。

public staticsingleton getinstance()

}}returnsingleton;

}雙檢測鎖定的方式 是只有當物件未建立的時候才對請求加鎖,物件建立以後都不會上鎖,這樣有效的提公升了程式的效率,也可以保證只會建立乙個物件的例項。

dcl是完美的解決了單例模式中效能和資源浪費的問題,但是dcl在並發情下也會存在乙個問題,因為jvm指令是亂序的;

情況如下:

執行緒1呼叫getinstance 獲取物件例項,因為物件還是空未進行初始化,此時執行緒1會執行new singleton()進行物件例項化,而當執行緒1的進行new singleton()的時候jvm會生成三個指令。

指令1:分配物件記憶體。

指令2:呼叫構造器,初始化物件屬性。

指令3:構建物件引用指向記憶體。

因為編譯器會自作聰明的對指令進行優化, 指令優化後順序會變成這樣:

1、執行指令1:分配物件記憶體,

2、執行指令3:構建物件引用指向記憶體。

3、然後正好這個時候cpu 切到了執行緒2工作,而執行緒2此時也呼叫getinstance獲取物件,那麼執行緒2將執行下面這個** if (singleton == null),此時執行緒2發現物件不為空(因為執行緒1已經建立物件引用並分配物件記憶體了),那麼執行緒2會得到乙個沒有初始化屬性的物件(因為執行緒1還沒有執行指令2)。

所以在這種情況下,雙檢測鎖定的方式會出現dcl失效的問題。

解決方案二:用內部類實現懶漢模式

public class singleton 

public static singleton getinstance()

//定義靜態內部類

private static class singletonholer

}

靜態內部類原理:當外部內被訪問時,並不會載入內部類,所以只要不訪問singletonholer 這個內部類, private static singleton singleton = new singleton() 不會例項化,這就相當於實現懶載入的效果,只有當singletonholer.singleton 被呼叫時訪問內部類的屬性,此時才會將物件進行例項化,這樣既解決了惡漢模式下可能造成資源浪費的問題,也避免了了懶漢模式下的併發問題。

寫乙個高併發下面的單例模式 設計模式之單例模式

乙個類只允許建立唯一乙個物件 或者例項 那這個類就是乙個單例類,這種設計模式就叫作單例設計模式,簡稱單例模式。經典的設計模式有 23 種,如果隨便抓乙個程式設計師,讓他說一說最熟悉的 3 種設計模式,那其中肯定會包含今天要講的單例模式,單例模式主要用來確保某個型別的例項只能有乙個。比如手機上的藍芽之...

寫乙個高併發下面的單例模式 go語言併發之鎖的基操

go語言除了使用goroutine和channel這種csp模型,也支援傳統的併發模型,今天我們就說一下go中的鎖的使用。互斥鎖是傳統併發對共享的資源進行訪問空值的主要手段,在 sync 包中,mutex 結構體表示。該結構體具備兩個公共的方法,lock和unlock,前者鎖定當前的互斥量,後者是解...

高併發下列舉單例執行緒安全?

先說結果,不是安全的 展示下列舉單例 package com.self.entity public enum logsingleton public logsingleton add override public string tostring 然後是呼叫方法 package com.self.t...