先來看看雙重檢測鎖的實現以及一些簡要的說明(本文主要說明雙重檢測鎖帶來的線程安全問題):
/** * 單例模式之雙檢鎖 * @author ring2 * 懶漢式升級版 */ public class Singleton3 { private static volatile Singleton3 instance; private Singleton3() {} public static Singleton3 getInstance() { //首先判斷是否為空 if(instance==null) { //可能多個線程同時進入到這一步進行阻塞等待 synchronized(Singleton3.class) { //第一個線程拿到鎖,判斷不為空進入下一步 if(instance==null) { /** * 由於編譯器的優化、JVM的優化、操作系統處理器的優化,可能會導致指令重排(happen-before規則下的指令重排,執行結果不變,指令順序優化排列) * new Singleton3()這條語句大致會有這三個步驟: * 1.在堆中開辟對象所需空間,分配內存地址 * 2.根據類加載的初始化順序進行初始化 * 3.將內存地址返回給棧中的引用變量 * * 但是由於指令重排的出現,這三條指令執行順序會被打亂,可能導致3的順序和2調換 * 👇 */ instance = new Singleton3(); } } } return instance; } }
由於指令重排導致3,2的順序調換以及處於多線程場景,會導致以下問題的出現首先第一個線程執行到了3號指令(instance變量被分配了地址,不為null了),但對象未初始化。此時!第一個或者第二個if語句進行判斷時結果為true,自然而然在使用instance時會出錯。
解決的方法便是在instance變量上加上volatile關鍵字,加上volatile關鍵字后會禁止該變量的指令重排,從而達到線程安全。關於volatile關鍵字。。。又可以說上一篇。在其他篇章。