亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java中使用 Volatile實現單例模式的方法

發布時間:2020-11-05 15:01:51 來源:億速云 閱讀:203 作者:Leah 欄目:開發技術

Java中使用 Volatile實現單例模式的方法?相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。

單例模式

回顧一下,單線程下的單例模式代碼

餓漢式

  • 構造器私有化
  • 自行創建,并且用靜態變量保存static
  • 向外提供這個實例 public
  • 強調這是一個單例,用final
public class sington(){
  public final static INSTANCE = new singleton();
  private singleton(){}
}

第二種:jdk1.5之后用枚舉類型

枚舉類型:表示該類型的對象是有限的幾個

我們可以限定為1個,就稱了單例

public enum Singleto{
  INSTANCE
}

第三種靜態代碼塊

public class Singleton{
public final static INSTANCE;
static{
  INSTANCE = new Singleton();
}
private Singleton(){}

}

懶漢式構造器私有化

用一個靜態變量保存這個唯一實例

提供一個靜態方法,獲取這個實例

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

  private static SingletonDemo instance = null;

  private SingletonDemo () {
    System.out.println(Thread.currentThread().getName() + "\t 我是構造方法SingletonDemo");
  }

  public static SingletonDemo getInstance() {
    if(instance == null) {
      instance = new SingletonDemo();
    }
    return instance;
  }

  public static void main(String[] args) {
    // 這里的 == 是比較內存地址
    System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());
    System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());
    System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());
    System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());
  }
}

最后輸出結果:

Java中使用 Volatile實現單例模式的方法

但是在多線程的環境下,我們的單例模式是否還是同一個對象了

public class SingletonDemo {

  private static SingletonDemo instance = null;

  private SingletonDemo () {
    System.out.println(Thread.currentThread().getName() + "\t 我是構造方法SingletonDemo");
  }

  public static SingletonDemo getInstance() {
    if(instance == null) {
      instance = new SingletonDemo();
    }
    return instance;
  }

  public static void main(String[] args) {
    for (int i = 0; i < 10; i++) {
      new Thread(() -> {
        SingletonDemo.getInstance();
      }, String.valueOf(i)).start();
    }
  }
}

從下面的結果我們可以看出,我們通過SingletonDemo.getInstance() 獲取到的對象,并不是同一個,而是被下面幾個線程都進行了創建,那么在多線程環境下,單例模式如何保證呢?

Java中使用 Volatile實現單例模式的方法

解決方法一

引入synchronized關鍵字

public synchronized static SingletonDemo getInstance() {
  if(instance == null) {
    instance = new SingletonDemo();
  }
  return instance;
}

輸出結果:

Java中使用 Volatile實現單例模式的方法

我們能夠發現,通過引入Synchronized關鍵字,能夠解決高并發環境下的單例模式問題

但是synchronized屬于重量級的同步機制,它只允許一個線程同時訪問獲取實例的方法,但是為了保證數據一致性,而減低了并發性,因此采用的比較少

解決方法二

通過引入DCL Double Check Lock雙端檢鎖機制

  public static SingletonDemo getInstance() {
    if(instance == null) {
      // 同步代碼段的時候,進行檢測
      synchronized (SingletonDemo.class) {
        if(instance == null) {
          instance = new SingletonDemo();
        }
      }
    }
    return instance;
  }

最后輸出的結果為:

Java中使用 Volatile實現單例模式的方法

從輸出結果來看,確實能夠保證單例模式的正確性,但是上面的方法還是存在問題的

DCL(雙端檢鎖)機制不一定是線程安全的,原因是有指令重排的存在,加入volatile可以禁止指令重排

原因是在某一個線程執行到第一次檢測的時候,讀取到 instance 不為null,instance的引用對象可能沒有完成實例化。因為 instance = new SingletonDemo();可以分為以下三步進行完成:

  • memory = allocate(); // 1、分配對象內存空間
  • instance(memory); // 2、初始化對象
  • instance = memory; // 3、設置instance指向剛剛分配的內存地址,此時instance != null

但是我們通過上面的三個步驟,能夠發現,步驟2 和 步驟3之間不存在 數據依賴關系,而且無論重排前 還是重排后,程序的執行結果在單線程中并沒有改變,因此這種重排優化是允許的。

  • memory = allocate(); // 1、分配對象內存空間
  • instance = memory; // 3、設置instance指向剛剛分配的內存地址,此時instance != null,但是對象還沒有初始化完成
  • instance(memory); // 2、初始化對象

這樣就會造成什么問題呢?

也就是當我們執行到重排后的步驟2,試圖獲取instance的時候,會得到null,因為對象的初始化還沒有完成,而是在重排后的步驟3才完成,因此執行單例模式的代碼時候,就會重新在創建一個instance實例

指令重排只會保證串行語義的執行一致性(單線程),但并不會關系多線程間的語義一致性

所以當一條線程訪問instance不為null時,由于instance實例未必已初始化完成,這就造成了線程安全的問題

所以需要引入volatile,來保證出現指令重排的問題,從而保證單例模式的線程安全性

private static volatile SingletonDemo instance = null;

最終代碼

public class SingletonDemo {

  private static volatile SingletonDemo instance = null;

  private SingletonDemo () {
    System.out.println(Thread.currentThread().getName() + "\t 我是構造方法SingletonDemo");
  }

  public static SingletonDemo getInstance() {
    if(instance == null) {
      // a 雙重檢查加鎖多線程情況下會出現某個線程雖然這里已經為空,但是另外一個線程已經執行到d處
      synchronized (SingletonDemo.class) //b
      { 
      //c不加volitale關鍵字的話有可能會出現尚未完全初始化就獲取到的情況。原因是內存模型允許無序寫入
        if(instance == null) { 
        	// d 此時才開始初始化
          instance = new SingletonDemo();
        }
      }
    }
    return instance;
  }

  public static void main(String[] args) {
//    // 這里的 == 是比較內存地址
//    System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());
//    System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());
//    System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());
//    System.out.println(SingletonDemo.getInstance() == SingletonDemo.getInstance());

    for (int i = 0; i < 10; i++) {
      new Thread(() -> {
        SingletonDemo.getInstance();
      }, String.valueOf(i)).start();
    }
  }
}

看完上述內容,你們掌握Java中使用 Volatile實現單例模式的方法的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

密山市| 榕江县| 乐平市| 富源县| 中卫市| 富蕴县| 塔河县| 英吉沙县| 江城| 千阳县| 集安市| 宣威市| 辽宁省| 太康县| 天津市| 汉川市| 陵水| 洪湖市| 红原县| 天等县| 内黄县| 修文县| 合山市| 和平县| 汶川县| 宕昌县| 文安县| 阿克苏市| 长海县| 昆山市| 梨树县| 东城区| 隆尧县| 白朗县| 青神县| 忻州市| 建湖县| 密山市| 白玉县| 繁峙县| 宣城市|