單例模式下為什麼一定要加volatile關鍵字
有一道面試題,單例模式已經雙重檢查鎖定(Double-Check-Lock)了,要不要加volatile關鍵字。以下是雙重檢查鎖定來實現單例模式程式碼
public class DoubleCheckLock {
private static DoubleCheckLock INSTANCE;
private DoubleCheckLock() {
}
public static DoubleCheckLock getInstance() {
if (INSTANCE == null) {
synchronized (DoubleCheckLock.class) {
if (INSTANCE == null) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new DoubleCheckLock();
}
}
}
return INSTANCE;
}
}
先介紹下volatile關鍵字的特性,一個變數被volatile修飾後具備兩個特性
- 可見性:此變數對所有執行緒可見,當一個執行緒修改了這個變數的值,新值對其他執行緒是可以立即得知的,如果不是volatile修飾的變數的值線上程間傳遞均需要通過主記憶體來完成,比如執行緒A修改了普通變數的值,然後向主記憶體進行回寫,另一條執行緒B線上程A回寫完成之後再對主記憶體進行讀取操作,新變數值才會對執行緒B可見。
- 禁止指令重排序優化:普通的變數僅會保證該方法的執行過程中所有依賴賦值結果的地方都能獲取到正確的結果,而不能保證變數賦值操作的順序與程式程式碼中的執行順序一致。
先試著解釋下如果不加volatile會有什麼問題,第一個執行緒過來,檢查發現其他執行緒沒有初始化然後就加上鎖,上鎖後對這個INSTANCE進行初始化,可是在這個過程中我們new了這個物件並且申請了記憶體,申請完記憶體裡邊的成員變數已經賦了初始值0,還沒有進行初始化,但這個時候INSTANCE就已經指向記憶體了,所以這個INSTATNCE已經不等於空了,這種情況下里一個執行緒來了,來了之後他首先執行if (INSTANCE == null),這個時候他處於半初始化,不為空的狀態,第二個執行緒就直接使用這個初始值了,而不是用那個預設值,解決這個問題就要加上volatile。
使用jclasslib檢視getInstance方法的位元組碼,下面是 INSTANCE = new DoubleCheckLock();
的位元組碼指令
new
dup
invokespecial
putstatic
- new指令在java堆上為物件分配記憶體空間,並將地址壓入運算元棧頂
- dup指令為複製運算元棧頂值,並將其壓入棧定,這是運算元棧有連續相同的兩個物件地址
- invokespecial呼叫例項的建構函式,這時會彈出一個棧頂元素
- putstatic指令將物件地址賦值給變數t,這時也會彈出一個棧頂元素
invokespecial指令與putstatic指令發生重排序,使這個INSTANTCE進行了半初始化,才會導致出現問題
使用volatile主要是加了記憶體屏障,指令重排序時不能把後面的指令重排序到記憶體屏障之前的位置
public class DoubleCheckLock {
private volatile static DoubleCheckLock INSTANCE;
private DoubleCheckLock() {
}
public static DoubleCheckLock getInstance() {
if (INSTANCE == null) {
synchronized (DoubleCheckLock.class) {
if (INSTANCE == null) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
INSTANCE = new DoubleCheckLock();
}
}
}
return INSTANCE;
}
}
相關文章
- Volatile關鍵字&&DCL單例模式,volatile 和 synchronized 的區別單例模式synchronized
- Java併發程式設計——為什麼要用volatile關鍵字Java程式設計
- 為什麼要用單例模式?單例模式
- Volatile關鍵字
- Java基礎-static關鍵字和單例模式Java單例模式
- 單例模式(含執行緒鎖關鍵字)單例模式執行緒
- volatile關鍵字解析
- 為什麼一定要學習設計模式設計模式
- volatile關鍵字在Android中到底有什麼用?Android
- 快速理解 volatile 關鍵字
- Java volatile關鍵字作用Java
- 深入解析volatile關鍵字
- Java volatile關鍵字解析Java
- 兩張圖理解volatile關鍵字
- Java關鍵字volatile的理解Java
- volatile關鍵字的作用、原理
- Java volatile關鍵字最全總結:原理剖析與例項講解(簡單易懂)Java
- 單例模式中為什麼用列舉更好單例模式
- 好文推薦 | volatile關鍵字在Android中到底有什麼用?Android
- 另存為快捷鍵ctrl加什麼 電腦wordexcelpt另存為快捷鍵是什麼Excel
- 為什麼async/await關鍵字是如此重要AI
- java併發之volatile關鍵字Java
- Java併發—— 關鍵字volatile解析Java
- javascript new關鍵字做了什麼JavaScript
- 【面試普通人VS高手系列】volatile關鍵字有什麼用?它的實現原理是什麼?面試
- Volatile關鍵字與執行緒安全執行緒
- Java記憶體模型——volatile關鍵字Java記憶體模型
- 深入瞭解 Java 的 volatile 關鍵字Java
- C語言中volatile關鍵字的作用C語言
- 面試官最愛的volatile關鍵字面試
- volatile 關鍵字的工作機制
- 漫畫:什麼是單例模式?單例模式
- 微服務為什麼一定要用docker微服務Docker
- 微服務為什麼一定要上Docker?微服務Docker
- 你為什麼一定要學Python?Python
- 問一下單例模式。單例模式
- 一個具體的例子學習Java volatile關鍵字Java
- Java列舉:為什麼它是單例模式的最佳選擇?Java單例模式