✨你瞭解volatile,但你知道它在單例模式中的作用嗎?

語言: CN / TW / HK

小知識,大挑戰!本文正在參與“程式設計師必備小知識”創作活動

Code皮皮蝦 一個沙雕而又有趣的憨憨少年,和大多數小夥伴們一樣喜歡聽歌、遊戲,當然除此之外還有寫作的興趣,emm...,日子還很長,讓我們一起加油努力叭🌈

✨作用

以 雙重檢查單例模式為例,先上程式碼

```java public class Singleton {

public Singleton() {
}

private static volatile Singleton singleton;

public static Singleton getSingleton() {
    if (singleton == null) {
        synchronized (Singleton.class) {
            if (singleton == null) {
                singleton = new Singleton();
            }
        }
    }
    return singleton;
}

} ```

如上述程式碼所示,singleton 用了 volatile 修飾,而我們都知道 volatile 的作用

  1. 不能保證原子性
  2. 保證共享變數的可見性
  3. 禁止指令重排序


那麼在單例模式中,volatile的作用到底是什麼呢?

其實,在單例模式中,volatile的作用主要是 禁止指令重排序

那麼為什麼呢?

在上述程式碼中,singleton = new Singleton(); 可分解為以下步驟:

  1. 分配物件記憶體空間
  2. 初始化物件
  3. 設定 singleton 指向分配的記憶體地址


但是步驟2,3是可能交換的,也就是發生重排序,但是根據 Java語言規範 intra-thread semantics,它是允許那些在單執行緒內,不會改變單執行緒程式執行結果的重排序,也就是說,雖然步驟2,3發生重排序,但是對於單執行緒來說,初次訪問物件時,其結果都是正確的,所以即使重排序也無所謂了。



但是在多執行緒環境中,這就會出現問題,例如,如下多執行緒發生重排序案例:

| 時間 | 執行緒A | 執行緒B | | ---- | --------------------------------- | -------------------------------------------------------- | | t1 | 分配物件空間 | | | t2 | 設定 singleton 指向分配的記憶體空間 | | | t3 | | 判斷 singleton 是否為空 | | t4 | | 由於 singleton 不為null,執行緒B將訪問singleton 引用的物件 | | t5 | 初始化物件 | | | t6 | 訪問 singleton 引用的物件 | |


有上述表格流程可以看出,在發生重排序的情況下,會導致執行緒B在 t3 時間下,判斷出 singleton 不為null,那麼執行緒B就會拿到這個 singleton 去做別的事,那麼此時這個 singleton 沒有初始化,那麼就會報錯,這就出了問題。


正常流程而言,在t2時間執行緒A應該對 singleton初始化,那麼執行緒B在


可能會有小夥伴就差臨門一腳的感覺就懂了,那我根據程式碼再說一遍

```java public class Singleton {

public Singleton() {
}

private static volatile Singleton singleton;

public static Singleton getSingleton() {
    if (singleton == null) {
        synchronized (Singleton.class) {
            if (singleton == null) {
                singleton = new Singleton();
            }
        }
    }
    return singleton;
}

} ```

根據程式碼而言,如果執行緒A在 第12行 即 singleton = new Singleton();發生重排序,那麼執行緒B在 第9行即第一個if (singleton == null)處就有可能判斷出 singleton不為null,進而拿到返回的 singleton 去做別的事進而導致報錯。



講到這裡相信各位小夥伴差不多都懂了,如果還不懂可以多看兩遍。

所以為了解決這個問題,就使用 volatile來修飾 singleton,來禁止重排序而可能出現的問題。

😉精選專欄

毛遂自薦,給大家推薦一下自己的專欄😁,歡迎小夥伴們收藏關注😊

小白學Java

MybatisPlus專欄

App爬蟲專欄

PC端爬蟲專欄

大廠面試題專欄


❤最後

我是 Code皮皮蝦,一個熱愛分享知識的 皮皮蝦愛好者,未來的日子裡會不斷更新出對大家有益的博文,期待大家的關注!!!

創作不易,如果這篇博文對各位有幫助,希望各位小夥伴可以==一鍵三連哦!==,感謝支援,我們下次再見~~~


一鍵三連.png