JAVA中單例模式的幾種實現方式

1 線程不安全的實現方法

首先介紹java中最基本的單例模式實現方式,咱們能夠在一些初級的java書中看到。這種實現方法不是線程安全的,因此在項目實踐中若是涉及到線程安全就不會使用這種方式。可是若是不須要保證線程安全,則這種方式仍是不錯的,由於所須要的開銷比較小。下面是具體的實現代碼:java

 

public Class Singleton
{
  private static Singleton instance = null;
  private Singleton(){}
  public  static Singleton getInstance()
  {
     if( instance == null)
        instance = new Singleton ();
     return instance;
  }
}    

 

咱們說過這種實現方式不是thread-safe的,那麼能夠把上面的方法變成線程安全的嗎?固然能夠,在方法getInstance()上加上synchronized修飾符就能夠實現方法的同步了。可是這樣系統開銷會很大。具體代碼以下:程序員

 

public Class Singleton
{
  private static Singleton instance = null;
  private Singleton(){}
  public  static synchronized  Singleton getInstance()
  {
     if( instance == null)
        instance = new Singleton ();
     return instance;
  }
}    

每次有線程調用getInstance()方法,都須要同步判斷。這顯然不是最好的選擇,下面將會陸續介紹幾種thread-safe的方法。安全

 

2 兩種lazy loaded thread-safe的單例模式實現方式

1) DCL (double checked locking 實現法)

    double checked locking ,顧名思義,就是雙檢查法,檢查實例INSTANCE是否爲null或者已經實例化了。下面是具體的實現代碼:多線程

 1 public class DoubleCheckedLockingSingleton{
 2      private volatile DoubleCheckedLockingSingleton INSTANCE;
 3  
 4      private DoubleCheckedLockingSingleton(){}
 5  
 6      public DoubleCheckedLockingSingleton getInstance(){
 7          if(INSTANCE == null){
 8             synchronized(DoubleCheckedLockingSingleton.class){
 9                 //double checking Singleton instance
10                 if(INSTANCE == null){
11                     INSTANCE = new DoubleCheckedLockingSingleton();
12                 }
13             }
14          }
15          return INSTANCE;
16      }
17 }

這種方法也很好理解,咱們能夠看到有兩次對instance是否爲null的判斷:若是第一次判斷不爲空,則直接返回實例就能夠了;若是instance爲空,則進入同步代碼塊再進行null值判斷,再選擇是否實例化。第一個null判斷能夠減小系統的開銷。在實際項目中作過多線程開發的都應該知道DCL。性能

 

2) lazy initialization holder class 模式實現法

下面是這種方法的實現代碼:spa

public class Singleton {
    /**
     * 類級的內部類,也就是靜態的成員式內部類,該內部類的實例與外部類的實例
     * 沒有綁定關係,並且只有被調用到纔會裝載,從而實現了延遲加載
     */
    private static class SingletonHolder{
        /**
         * 靜態初始化器,由JVM來保證線程安全
         */
        private static Singleton instance = new Singleton();
    }
    /**
     * 私有化構造方法
     */
    private Singleton(){
    }
    public static  Singleton getInstance(){
        return SingletonHolder.instance;
    }
}

 

當getInstance方法第一次被調用的時候,它第一次讀取SingletonHolder.instance,致使SingletonHolder類獲得初始化;而這個類在裝載並被初始化的時候,會初始化它的靜態域,從而建立Singleton的實例,因爲是靜態的域,所以只會被虛擬機在裝載類的時候初始化一次,並由虛擬機來保證它的線程安全性。這個模式的優點在於,getInstance方法並無被同步,而且只是執行一個域的訪問,所以延遲初始化並無增長任何訪問成本。
線程

 

關於延遲初始化(lazy loadedcode

「除非絕對必要,不然就不要延遲初始化」。延遲初始化是一把雙刃劍,它下降了初始化類或者建立實例的開銷,卻增長了訪問被延遲初始化的域的開銷,考慮到延遲初始化的域最終須要初始化的開銷以及域的訪問開銷,延遲初始化實際上下降了性能。對象

 

3 靜態工廠實現法

  由於單例是靜態的final變量,當類第一次加載到內存中的時候就初始化了,其thread-safe性由JVM來負責保證。值得注意的是這個實現方式不是lazy-loadedd的。   具體實現代碼以下:blog

 1 public class Singleton{
 2     //initailzed during class loading
 3     private static final Singleton INSTANCE = new Singleton();
 4  
 5     private Singleton(){}
 6  
 7     public static Singleton getSingleton(){
 8         return INSTANCE;
 9     }
10 }

 

枚舉實現單例(Enum Singleton)

  枚舉單例(Enum Singleton)是實現單例模式的一種新方式,枚舉這個特性是在Java5纔出現的,在《Effective Java》一書中有介紹這個特性。下面是這種方法的具體實現代碼:

public enum Singleton {  
    INSTANCE("hello") {  
        public void someMethod() {  
            // . . .  
        }  
    };  
    private String name;
    private void PrintName(){System.out.println(name);}
    protected abstract void someMethod();  
} 

你能夠經過Singleton.INSTANCE來訪問該單示例變量。默認枚舉實例的建立是線程安全的,可是在枚舉中的其餘任何方法由程序員本身負責。若是你正在使用實例方法,那麼你須要確保線程安全(若是它影響到其餘對象的狀態的話)。傳統單例存在的另一個問題是一旦你實現了序列化接口,那麼它們再也不保持單例了,可是枚舉單例,JVM對序列化有保證。枚舉實現單例的好處:有序列化和線程安全的保證,代碼簡單

相關文章
相關標籤/搜索