設計模式專輯——單例的懶漢模式和餓漢模式

>>本文轉自「Java知音「安全

 

1.懶漢模式微信

代碼1
//懶漢式單例模式
//在類加載時,不建立實例,所以類加載速度快,但運行時獲取對象的速度慢
public
class LazySingleton{ private static LazySingleton instance = null; private LazySingleton(){} public static synchronized LazySingleton getInstance(){ if(instance == null){ instance = new LazySingleton(); } return instance; } }

代碼1中在getInstance上加同步鎖的方式,十分影響效率。對此,出現了以下雙重檢查的方式優化

代碼2
public
class LazySingleton{ private static LazySingleton instance = null; private LazySingleton(){} public static LazySingleton getInstance(){ if(instance == null){ synchronized(LazySingleton.class){ if(instance==null){ instance = new LazySingleton(); } } } return instance; } }

使用如代碼2的雙重檢查模式,使得只在instance爲null時才須要加同步鎖,從而提升了效率。spa

可是代碼2存在一個細微的問題,即instance = new LazySingleton()並非一個原子操做,它包括如下三步原子操做:線程

1.爲實例對象分配內存code

2.對象初始化對象

3.instance指向分配的內存blog

在指令重排的狀況下,第3步可能會在第2步以前執行。排序

此時在其餘線程看來instance已經非null,致使getInstance方法返回未初始化完成的instance對象。內存

爲了解決這個問題,須要引入volatile關鍵字,代碼以下

代碼3
public class LazySingleton{
  private static volatile LazySingleton instance = null;

  private LazySingleton(){}

  public static LazySingleton getInstance(){
    if(instance == null){
      synchronized(LazySingleton.class){
        if(instance==null){
           instance = new LazySingleton();
        }
      } 
    }
    return instance;
  }      
}

volatile能保證可見性和有序性。

在這裏volatile保證了有序性。

volatile關鍵字禁止指令重排序有兩層意思:

1)當程序執行到volatile變量的讀操做或者寫操做時,在其前面的操做的更改確定所有已經進行,且結果已經對後面的操做可見;在其後面的操做確定尚未進行;

2)在進行指令優化時,不能將在對volatile變量的讀操做或者寫操做的語句放在其後面執行,也不能把volatile變量後面的語句放到其前面執行

 

2.餓漢模式

代碼4
//餓漢單例模式
//類加載較慢,但獲取對象的速度快
//且線程安全
public
class EagerSingleton{ private static EagerSingleton instance = new EagerSingleton(); private EagerSingleton(){ ... } public static EagerSingleton getInstance(){ return instance; } }

餓漢模式線程安全,只不過在類加載時就進行了實例初始化,可能有點浪費。 

 

3.推薦方式

 綜上,考慮到延遲加載和線程安全,推薦使用如下方式

代碼5
//靜態內部類形式
public
class Singleton{ private static class SingletonHolder{ private static final Singleton INSTANCE = new Singleton(); } private Singleton(){} public static final Singleton getInstance(){ return SingletonHolder.INSTANCE; } }

這樣初始化延遲到getInstance方法被調用時,而且同步由ClassLoader來保證。

 

喜歡的話能夠打賞一下哦!!!

支付寶

微信

相關文章
相關標籤/搜索