一人一猫旅行记之浅析单例模式
單例模式是我們日常工作中接觸最多,也可以說是最簡單的一個設計模式。那么什么是單例呢?為什么要用這種模式?
單例簡單來說就是只進行一次初始化,以Imageloader為解釋為什么要用這種模式,因為ImageLoader會涉及到大量的io和網絡請求,消耗大量的資源,為了減少資源的消耗和浪費,便出現了單例模式。
我們常見的單例模式有很多種實現方式,比如餓漢模式、懶漢模式、雙加鎖模式等等,下面以簡單的例子來一一記錄這些單例模式的實現:
1、餓漢模式
為何稱之為“餓漢模式”呢?因為餓漢經常會突然的餓意襲來,為了在餓意到來的時候,有足夠的食物,餓漢需要提前將食物準備好。我們的“餓漢模式”也是這樣,在使用之前便為我們初始化了單例對象,需要注意的是初始化的時候,還沒有產生子線程,因此不會存在線程安全的問題。
2、懶漢模式
該模式是經常與餓漢一起提起的,為什么叫懶漢呢?是因為該模式下不會提前進行初始化,而是在需要的時候才會進行初始化。在這種模式下,需要注意的是高并發情況下,會出現線程安全問題,因此需要引入同步鎖!
“餓漢模式”與“懶漢模式”的區別在于初始化的時間不同,“餓漢模式”下無論是否會調用,都會進行初始化,造成資源的浪費,而“懶漢模式”只會在需要的時候初始化,不會造成資源浪費。
3、雙加鎖(DCL)模式
在“懶漢模式”,雖然不會每次調用getInstance都會進行初始化,但是每次調用都會使用同步鎖,也會造成不必要的浪費,因此出現了“雙加鎖模式”,代碼如下:
從上面的代碼,我們可以看到兩次判空操作,第一次判空是為了判斷是否需要加鎖,而第二次判斷則是為了保證只會初始化一次。這么看來,這種模式是不是一種最優的方案呢?實際上是不支持這種寫法的,因為會有一個叫做“重排序”的問題,重排序通常是編譯器或運行時環境為了優化程序性能而采取的對指令進行重新排序執行的一種手段。
正常情況下,INSTANCE = new DoubleCheckLocked();這句代碼可以分為三個步驟:
a.分配對象的內存空間
b.初始化對象
c.設置sInstance指向剛分配的內存地址
但是由于重排序的存在,在高并發的環境下,為了提高運行速率,可能會讓bc的步驟發生變化,這樣就有可能導致某個線程調用獲取到了一個還未初始化的對象。
JDK1.5之后針對上面的問題,官方對volatile進行了優化,因此在1.6之后也可以通過volatile實現。
4、靜態內部類實現單例
因為INSTANCE 的定義是private static final,因此只會在第一次調用的時候才會初始化,因此不會涉及線程安全問題。另外外部類加載的時候并不會進行初始化,因此不會占用內存。
當然除了上面四種方式,我們還可以通過枚舉或者一些容器,如HashMap等實現單例,枚舉無需解釋,本身枚舉就是為單例而生,而使用容器等可能會涉及到線程安全問題,在某些場景下使用需要思考清楚。
5、枚舉實現單例
6、容器實現單例
public class Container {private static Map<String,Container> SINGLEMAP = new HashMap<>();public static void putSingle(String key,Container value){if(!SINGLEMAP.containsKey(key)){SINGLEMAP.put(key,value);}}public static Container getInstance(String key){return SINGLEMAP.get(key);} }綜上所述,實現單例的方法有很多種,其核心就是為了減少初始化的次數,從而減少資源的浪費,多用在涉及到IO、網絡請求等場景。道路千萬條,針對不同的場景,選擇最合適的方案!
總結
以上是生活随笔為你收集整理的一人一猫旅行记之浅析单例模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ARM9——五级流水线结构,以及PC指针
- 下一篇: dB、dBm、dBw的含义和转化关系