1. 程式人生 > >高併發環境下執行緒安全的單例模式(最全最經典)

高併發環境下執行緒安全的單例模式(最全最經典)

在所有的設計模式中,單例模式是我們在專案開發中最為常見的設計模式之一,而單例模式有很多種實現方式,你是否都瞭解呢?高併發下如何保證單例模式的執行緒安全性呢?如何保證序列化後的單例物件在反序列化後任然是單例的呢?這些問題在看了本文之後都會一一的告訴你答案,趕快來閱讀吧!

什麼是單例模式?

在文章開始之前我們還是有必要介紹一下什麼是單例模式。單例模式是為確保一個類只有一個例項,併為整個系統提供一個全域性訪問點的一種模式方法。

從概念中體現出了單例的一些特點:

(1)、在任何情況下,單例類永遠只有一個例項存在

(2)、單例需要有能力為整個系統提供這一唯一例項  

為了便於讀者更好的理解這些概念,下面給出這麼一段內容敘述:

在計算機系統中,執行緒池、快取、日誌物件、對話方塊、印表機、顯示卡的驅動程式物件常被設計成單例。這些應用都或多或少具有資源管理器的功能。每臺計算機可以有若干個印表機,但只能有一個Printer Spooler,以避免兩個列印作業同時輸出到印表機中。每臺計算機可以有若干通訊埠,系統應當集中管理這些通訊埠,以避免一個通訊埠同時被兩個請求同時呼叫。總之,選擇單例模式就是為了避免不一致狀態,避免政出多頭。

正是由於這個特點,單例物件通常作為程式中的存放配置資訊的載體,因為它能保證其他物件讀到一致的資訊。例如在某個伺服器程式中,該伺服器的配置資訊可能存放在資料庫或檔案中,這些配置資料由某個單例物件統一讀取,服務程序中的其他物件如果要獲取這些配置資訊,只需訪問該單例物件即可。這種方式極大地簡化了在複雜環境 下,尤其是多執行緒環境下的配置管理,但是隨著應用場景的不同,也可能帶來一些同步問題。
    

各式各樣的單例實現

溫馨提示:本文敘述中涉及到的相關原始碼可以在這裡進行下載原始碼,讀者可免積分下載。

1、餓漢式單例

餓漢式單例是指在方法呼叫前,例項就已經建立好了。下面是實現程式碼:

  1. package org.mlinge.s01;  
  2. publicclass MySingleton {  
  3.     privatestatic MySingleton instance = new MySingleton();  
  4.     private MySingleton(){}  
  5.     publicstatic MySingleton getInstance() {  
  6.         return instance;  
  7.     }  
  8. }  
以上是單例的餓漢式實現,我們來看看餓漢式在多執行緒下的執行情況,給出一段多執行緒的執行程式碼:
  1. package org.mlinge.s01;  
  2. publicclass MyThread extends Thread{  
  3.     @Override
  4.     publicvoid run() {   
  5.         System.out.println(MySingleton.getInstance().hashCode());  
  6.     }  
  7.     publicstaticvoid main(String[] args) {   
  8.         MyThread[] mts = new MyThread[10];  
  9.         for(int i = 0 ; i < mts.length ; i++){  
  10.             mts[i] = new MyThread();  
  11.         }  
  12.         for (int j = 0; j < mts.length; j++) {  
  13.             mts[j].start();  
  14.         }  
  15.     }  
  16. }  

以上程式碼執行結果:

  1. 1718900954  
  2. 1718900954  
  3. 1718900954  
  4. 1718900954  
  5. 1718900954  
  6. 1718900954  
  7. 1718900954  
  8. 1718900954  
  9. 1718900954  
  10. 1718900954  

從執行結果可以看出例項變數額hashCode值一致,這說明物件是同一個,餓漢式單例實現了。

2、懶漢式單例

懶漢式單例是指在方法呼叫獲取例項時才建立例項,因為相對餓漢式顯得“不急迫”,所以被叫做“懶漢模式”。下面是實現程式碼:

  1. package org.mlinge.s02;  
  2. publicclass MySingleton {  
  3.     privatestatic MySingleton instance = null;  
  4.     private MySingleton(){}  
  5.     publicstatic MySingleton getInstance() {  
  6.         if(instance == null){//懶漢式
  7.             instance = new MySingleton();  
  8.         }  
  9.         return instance;  
  10.     }  
  11. }  
這裡實現了懶漢式的單例,但是熟悉多執行緒併發程式設計的朋友應該可以看出,在多執行緒併發下這樣的實現是無法保證例項例項唯一的,甚至可以說這樣的失效是完全錯誤的,下面我們就來看一下多執行緒併發下的執行情況,這裡為了看到效果,我們對上面的程式碼做一小點修改:
  1. package org.mlinge.s02;  
  2. publicclass MySingleton {  
  3.     privatestatic MySingleton instance = null;  
  4.     private MySingleton(){}  
  5.     publicstatic MySingleton getInstance() {  
  6.         try {   
  7.             if(instance != null){//懶漢式 
  8.             }else{  
  9.                 //建立例項之前可能會有一些準備性的耗時工作 
  10.                 Thread.sleep(300);  
  11.                 instance = new MySingleton();  
  12.             }  
  13.         } catch (InterruptedException e) {   
  14.             e.printStackTrace();  
  15.         }  
  16.         return instance;  
  17.     }  
  18. }  
這裡假設在建立例項前有一些準備性的耗時工作要處理,多執行緒呼叫:
  1. package org.mlinge.s02;  
  2. publicclass MyThread extends Thread{  
  3.     @Override
  4.     publicvoid run() {   
  5.         System.out.println(MySingleton.getInstance().hashCode());  
  6.     }  
  7.     publicstaticvoid main(String[] args) {   
  8.         MyThread[] mts = new MyThread[10];  
  9.         for(int i = 0 ; i < mts.length ; i++){  
  10.             mts[i] = new MyThread();  
  11.         }  
  12.         for (int j = 0; j < mts.length; j++) {  
  13.             mts[j].start();  
  14.         }  
  15.     }  
  16. }  

執行結果如下:

  1. 1210420568  
  2. 1210420568  
  3. 1935123450  
  4. 1718900954  
  5. 1481297610  
  6. 1863264879  
  7. 369539795  
  8. 1210420568  
  9. 1210420568  
  10. 602269801  

從這裡執行結果可以看出,單例的執行緒安全性並沒有得到保證,那要怎麼解決呢?

3、執行緒安全的懶漢式單例

要保證執行緒安全,我們就得需要使用同步鎖機制,下面就來看看我們如何一步步的解決 存線上程安全問題的懶漢式單例(錯誤的單例)。

(1)、 方法中宣告synchronized關鍵字

出現非執行緒安全問題,是由於多個執行緒可以同時進入getInstance()方法,那麼只需要對該方法進行synchronized的鎖同步即可:

  1. package org.mlinge.s03;  
  2. publicclass MySingleton {  
  3.     privatestatic MySingleton instance = null;  
  4.     private MySingleton(){}  
  5.     publicsynchronizedstatic MySingleton getInstance() {  
  6.         try {   
  7.             if(instance != null){//懶漢式 
  8.             }else{  
  9.                 //建立例項之前可能會有一些準備性的耗時工作 
  10. 相關推薦

    併發環境執行安全模式經典

    在所有的設計模式中,單例模式是我們在專案開發中最為常見的設計模式之一,而單例模式有很多種實現方式,你是否都瞭解呢?高併發下如何保證單例模式的執行緒安全性呢?如何保證序列化後的單例物件在反序列化後任然是單例的呢?這些問題在看了本文之後都會一一的告訴你答案,趕快來閱讀吧!什麼是單

    C++的模式執行安全模式懶漢/餓漢

    單例模式 單例模式:是一種常用的軟體設計模式。在它的核心結構中只包含一個被稱為單例的特殊類。通過單例模式可以保證系統中一個類只有一個例項。即一個類只有一個物件例項。   實現簡單的單例模式:建構函式宣告為private或protect防止被外部函式

    [C++][執行安全]模式雙檢查鎖和執行

    問題 在設計模式中,有一個很經典的模式-單例模式,它可能是實現上最簡單的模式,在程式碼中也經常使用,在單執行緒下,毫無疑問延遲化載入是比較常用的,但是在多執行緒條件下,單例模式的延遲載入可能就會出現一些問題。 如以下的程式碼: T* GetInstance(

    併發程式設計:執行安全和ThreadLocal

    執行緒安全的概念:當多個執行緒訪問某一個類(物件或方法)時,這個類始終都能表現出正確的行為,那麼這個類(物件或方法)就是執行緒安全的。 執行緒安全 說的可能比較抽象,下面就以一個簡單的例子來看看什麼是執行緒安全問題。 public class MyThread impleme

    Java設計模式模式的5種實現方式,以及在多執行環境5種建立模式的效率

    這段時間從頭溫習設計模式。記載下來,以便自己複習,也分享給大家。 package com.iter.devbox.singleton; /** * 餓漢式 * @author Shearer * */ public class SingletonDemo1 {

    執行安全設計模式+序列化

    懶漢式單例模式會引來執行緒安全問題,即多執行緒時可能會建立多個例項,而且在反序列化時也可能會建立新的例項。看了大大們的實現方式,覺得以下一種比較簡單方便理解。 一、下面是會造成執行緒安全的餓漢單例模式。用四列印下會發現hashcode不一致 public class Singleton {

    高效能的執行安全——Java基礎語法系列

    大家都知道,一般實現單例有兩種寫法: 餓漢式 和 懶漢式, 餓漢式是執行緒安全的,在編譯期間已完成初始化,載入到了記憶體裡。 懶漢式一般寫法是非執行緒安全的, 那懶漢式的執行緒安全單例應該如何實現呢,以及如何寫出低耗能的執行緒安全單例呢 ? 單例實現關鍵點 建構函式私有,

    Qt實現支援多執行模式

    1. 程式碼介紹 實現單例模式的程式碼很多。 本文的單例模式實現程式碼是本人一直在工程專案中使用的,現拿出和大家交流分享。 本文實現的單例模式,支援多執行緒,採用雙重校驗檢索的方式,整合析構類,杜絕記憶體洩漏,穩定性好。 使用C++/Qt的朋友們可以瞭解一下。 不再廢話,直接上程式碼。

    java通過雙重檢測或列舉類實現執行安全(懶漢模式)

    雙重檢測實現 /** * 懶漢模式->雙重同步鎖單例模式 */ public class SingletonExample5 { private SingletonExample5() { } //volatile + 雙重檢測機制 -> 禁止指令重排序

    執行安全(與多)

          又週五了,時間過得好快,住在集體宿舍,幾個宅男共處一室好是無聊,習慣性來到CSDN。今天一個應屆生同事突然問我為什麼老大要求我們不要在Service裡寫成員變數,說不安全,說為什麼不安全讓他自己去了解,看上去他沒有找到頭緒很是痛苦,想想當初這個問題也困擾過自己,向

    執行涉及模式

    /** * 單例模式涉及的兩個問題 * * @author 羅摩銜那 * */ /* * 惡漢式 * 優點:當類被載入的時候,已經建立好了一個靜態的物件,因此,是執行緒安全的 * 缺點:這個物件還沒有被使用就被創建出來了 */ class Single { private s

    日常小結-多執行模式的三種實現方式

    多執行緒單例模式在很多併發的書裡面都有寫。這裡做一個簡單的總結。主要內容來自《java併發程式設計的藝術》《java多執行緒程式設計核心》 單例模式的分類 餓漢模式:類初始化的時候就進行建立單例模式 懶漢模式:在呼叫getinstance方法的時候才建

    JAVA_多執行_模式

    這篇是入職之後的第二篇了,上一篇我簡單介紹了一下LOCK裡面的類的方法,感興趣的話可以去了解一下,以後堅持每週至少會更新一篇關於多執行緒方面的文章,希望博友們可以一起加油成長。 這篇主要的內容是單例模式在多執行緒環境下的設計,這篇算是比較重要的內容,我會進行文字和程式碼的共同說明來講解記錄 1、立即載入(餓

    【Java多執行模式與多執行

    單例模式大家都不陌生,即讓一個類只有一個例項。 單例模式分為懶漢式和餓漢式。 懶漢式☞方法呼叫時再例項化物件,什麼時候用什麼時候例項化,比較懶。 餓漢式☞方法呼叫前物件就已經建立好了,比較有捉急。 本文著重描述懶漢式與多執行緒的內容。 1.餓漢式 public

    Java多執行模式-yellowcong

    我們常見的單利模式有餓漢式和懶漢式,懶漢式,就是在用的時候,再例項化物件,餓漢式,是還沒有開飯,就已經把物件例項化好了。在多執行緒開發中,解決單例的時候,有兩種解決方案,1、(懶漢式)同步程式碼塊

    模式懶漢,餓漢

    ati turn 還需 sin 有用 只需要 對象 clas main Java中的單例模式一般分為懶漢模式和餓漢模式,懶漢模式只有用得到的時候對象才初始化,餓漢模式無論用得到與否,都先初始化。 懶漢模式在運行的時候獲取對象比較慢(因為類加載時並沒有創建對象實例),但是加載

    java模式雙重檢查加鎖的原因

    csharp sta get 第一次 instance new 同步機制 原因 AR public class Singleton{ private static Singleton instance = null;//是否是final的不重要,因為最多只可能實

    模式餓漢式 & 懶漢式

    建立型模式——單例模式 -目的:使得類的一個物件成為該類系統中的唯一例項。 -定義:一個類有且僅有一個例項,並且自行例項化向整個系統提供。 優點: 1、在記憶體中只有一個物件,節省記憶體空間。 2、

    C++實現一個模式懶漢與餓漢

    單例模式的特點: 1、一個類只能有一個例項。 2、一個類必須自己建立自己的唯一例項。 3、一個類必須給所有其他物件提供這一例項。 單例模式的實現: 1、將建構函式宣告為private防止被外部

    Java 模式餓漢+懶漢

    java單例就是一個類始終只例項化一次 餓漢模式:在程式啟動,類載入的時候就初始化: public class Singleton{ private static Singleton instance = new Singleton(); private S