線程安全的單例模式


單例模式是一種常用的設計模式,其定義是單例對象的類只能允許一個實例存在。下面來看看幾種常見的單例模式的寫法,以及如何保證線程安全的實現。

1、餓漢式(線程安全)

  這種寫法比較簡單,就是在類裝載的時候就完成實例化。避免了線程同步問題。但是在類裝載的時候就完成實例化,沒有達到懶加載的效果。如果從始至終從未使用過這個實例,則會造成內存的浪費。

public class Singleton {

    private static final Singleton instance = new Singleton();

    private Singleton(){}

    public static Singleton getInstance(){
        return instance ;
    }
}

2、懶漢式(線程安全)

  這種方式效率太低了,每個線程在想獲得類的實例的時候,執行getInstance()方法都要進行同步。而其實這個方法只執行一次實例化代碼就夠了,后面的想獲得該類實例,直接return就行了。也就是我們之前提到的同步的粒度太粗,synchronized 同步代碼應該是越細越好。

public class Singleton {

    private static Singleton singleton;

    private Singleton() {}

    public static synchronized Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

3、懶漢式(線程安全)的細粒度優化(雙重鎖機制)

  對上一種模式進行優化,這里判斷了兩次是否為 null 是因為在並發環境中當線程一執行了第一個判斷的時候是為null,可此刻另外一個線程正好執行完初始化操作,在釋放鎖以后該線程並不知道已經初始化,如果此刻進入代碼塊不進行再次判斷會再初始化一次,這就違背了單例模式的初衷了。

public class Singleton {  

     private static Singleton instance;
  
     private Singleton (){}   

     public static Singleton getInstance(){ 

       if (instance == null){
           synchronized(Singleton.class){
               if (instance == null)
                   instance = new Singleton(); 
           }
       }
       return instance;
     }
 }

4、靜態內部類(懶加載,線程安全)

  這種方式跟餓漢式方式采用的機制類似,但又有不同。兩者都是采用了類裝載的機制來保證初始化實例時只有一個線程。不同的地方在餓漢式方式是只要Singleton類被裝載就會實例化,沒有懶加載的作用,而靜態內部類方式在Singleton類被裝載時並不會立即實例化,而是在需要實例化時,調用getInstance方法,才會裝載SingletonInstance類,從而完成Singleton的實例化。類的靜態屬性只會在第一次加載類的時候初始化,所以在這里,JVM幫助我們保證了線程的安全性,在類進行初始化時,別的線程是無法進入的。避免了線程不安全,延遲加載,效率高。

public class Singleton {

    private Singleton() {}
    //內部類在外部類調用的時候才會被初始化
    // 內部類一定要在方法調用之前初始化
    private static class SingletonInstance {
        private static final Singleton instance = new Singleton();
    }
    // static 使單例空間共享
    // final使得方法不能被重寫重載
    public static final Singleton getInstance() {
        return SingletonInstance.instance;
    }
}

 

  這里可以在私有的構造方法中進行一個雙重鎖的判斷,定義一個 flag來判斷該構造是否被重復調用,來防止反射的侵入。

  除此之外還可以使用枚舉類的方式來實現單例模式。由於實際工作中並未發現有人這么做,這里就不演示了。


免責聲明!

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



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