線程安全的單例模式


1. 全局變量的缺點:

必須在程序一開始就創建好對象,如果程序在這次的執行過程中又一直沒用到它,就非常耗費資源。

 

2. 經典的單例模式實現:

Java代碼
public class Singleton {    
      //用一個靜態變量來記錄Singleton類的唯一實例   
      private static Singleton uniqueInstance;
      private Singleton() {}   
           
      //注意這個方法也是靜態的   
      public static Singleton getInstance() {    
           if(uniqueInstance == null) {   
             uniqueInstance = new Singleton();   
           }   
           return uniqueInstance;   
      }   
}  

    單例常被用來管理共享的資源,例如數據庫連接、線程池、緩存、注冊表。

    單例模式確保一個類只有一個實例,並提供一個全局訪問點。

    這個模式的問題:在多線程時,並不能保證這個類只被實例化一次。

 

3. 處理多線程:

Java代碼
public class Singleton {    
    //用一個靜態變量來記錄Singleton類的唯一實例   
    private static Singleton uniqueInstance;   
    
    private Singleton() {}   
           
    //注意這個方法也是靜態的   
    public static synchronized Singleton getInstance() {    
        if(uniqueInstance == null) {   
             uniqueInstance = new Singleton();   
         }   
         return uniqueInstance;   
    }   
}  

 

   通過增加synchronized關鍵字到getInstance()方法中,迫使每個線程在進入方法之前,要先等別的線程離開該方法。也就是說,不會有兩個線程可以同時進入這個方法。

 

   這種方法存在的問題:只有第一次執行此方法時,才真正需要同步。換句話說,一旦設置好uniqueInstance變量,就不再需要同步這個方法了。之后每次調用這個方法,同步都是一種浪費。

 

4.改善多線程

 

4.1 如果getInstance()的性能對應用程序不是很關鍵,就不用優化了

4.2 使用急切創建實例,而不用延遲實例化的做法

Java代碼
public class Singleton {
    private static Singleton uniqueInstance = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() {    
         return uniqueInstance;   
    }
}

   標紅的語句在靜態初始化器(static initializer)中創建單例,這保證了線程安全。

   利用這個做法,JVM在加載這個類時馬上創建此唯一的單件實例。JVM保證任何線程訪問uniqueInstance靜態變量之前,一定先創建些實例。

 

   4.3 用“雙重檢查加鎖”,在getInstance()中減少使用同步

    首先檢查實例是否已經創建,如果尚未創建,才進行同步。這樣一來,只有第一次會同步,這正是我們想要的。

Java代碼

 

public class Singleton {
    private volatile static Singleton uniqueInstance;
    private Singleton() {}
    public static Singleton getInstance() {    
    if(uniqueInstance == null) {//(1)
//只有第一次才徹底執行這里的代碼 
 synchronized() { //再檢查一次 
          if(uniqueInstance == null) uniqueInstance = new Singleton(); } } return uniqueInstance; } } 

   在最開始如果有1、2、3個線程走到了(1)處,假設1進入了同步塊,2、3等待。1實例化后,2進入同步塊,發現uniqueInstance已經不為空,跳出同步塊。接着3進入,又跳出同步塊。

    volatile關鍵字確保:當uniqueInstance變量被初始化成Singleton實例時,多個線程正確地uniqueInstance變量。如果性能是你關心的重點,那么這個做法可以幫你大大地減少getInstance()的時間耗費。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM