您好,登錄后才能下訂單哦!
這篇文章主要講解了“java雙重檢查鎖問題怎么解決”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“java雙重檢查鎖問題怎么解決”吧!
首先我們來看一下非線程安全的初始化單例模式
public class UnsafeLazyInitialization { private static UnsafeLazyInitialization instance; public static UnsafeLazyInitialization getInstance(){ if(instance == null){ //1: 線程A執行 instance = new UnsafeLazyInitialization(); //2: 線程B執行 } return instance; } }
在 UnsafeLazyInitialization 類中,假設線程A執行到代碼1的時候,線程B執行到代碼2, 這時候線程A 可能 看到 instance 引用對象還沒有完成初始化。
對于 UnsafeLazyInitialization 類,我們可以對getInstance()方法做同步處理來實現來實現線程安全的延遲初始化,示例代碼如下:
public static synchronized UnsafeLazyInitialization getInstance(){ if(instance == null){ //1: 線程A執行 instance = new UnsafeLazyInitialization(); //2: 線程B執行 } return instance; } }
由于上述代碼對getInstance()方法做了同步處理,這樣可能導致同步程序開銷加大。 如果getInstance()被多個線程頻繁調用,將會導致程序執行性能降低,反之如果不是被多個線程調用,那個這個getInstance()方法的延遲初始化方法將影響性能。
JVM 1.6之前 synchronized是重量級鎖,所以很耗費性能,所以人們想到了一個種雙重校驗鎖(Dobule-check Locking)的方案來提高性能,示例代碼如下:
public class DoubleCheckedLocking { //1、 private static Instance instance; //2、 public static Instance getInstance(){ //3、 if(instance == null){ //4、第一次檢查 synchronized (DoubleCheckedLocking.class){ //5、枷鎖 if(instance == null){ //6、第二次檢查 instance = new Instance(); //7、問題的根源在這里 } //8、 } } return instance; } }
如上代碼所示:如果 步驟4、第一次檢查instance不為null,則就不需要執行下面的加鎖操作,大大降低了synchronized 鎖帶來的性能問題。上面代碼看起來沒有任何問題。 1、多個線程視圖去創建新對象的時候,通過synchronized關鍵字可以保證只有一個線程創建對象成功。
2、如果instance 實例對象已經被創建,則直接通過getInstatnce()方法獲取對象實例。
上面代碼看上去很完美,但是當執行步驟4的時候,instatnce!=null 的時候,instatnce 的引用對象有可能還沒有完成初始化。
上面代碼我們執行到步驟7的時候,instance = new Instance(); ,創建了一個對象,這個創建對象的步驟可以分為三步,如下:
memory = allocate() //1.分配內存空間memory ctorInstance(memory) //2, 初始化對象在內存 分配內存空間memory上初始化 Singleton 對象 instance = memory //3、設置 instance 指向剛分配的內存地址memory
上面三行代碼 2和3可能發生重排序,在(JTI編譯器上,這種重排序是真是發生的) 步驟2和步驟3發生重排序后執行順序
memory = allocate() //1.分配內存空間memory instance = memory //3、設置 instance 指向剛分配的內存地址memory // 注意此時instance對象還沒有被初始化,但是instance的引用已經不是null了。 ctorInstance(memory) //2, 初始化對象在內存 分配內存空間memory上初始化 Singleton 對象
下面看一下多線程執行順序
上述代碼第7行instance = new Instance(); 如果A線程發生指令重排序(2,3),那么另一個線程B有可能在4行代碼判斷 instance 不為空。線程B接下來訪問instance的引用對象,但是instance對象有可能還沒被A初始化完成。此時線程B可能訪問一個沒有初始化完成的對象,導致報空指針錯誤。
1、不允許2、3進行指令重排。 2、允許2、3進行重排序,但是不允許其它線程看到重排序
基于上面代碼只需要在instance聲明時加上volatile關鍵字就可以,如下代碼
public class DoubleCheckedLocking { //1、 private static volatile Instance instance; //2、 public static Instance getInstance(){ //3、 if(instance == null){ //4、第一次檢查 synchronized (DoubleCheckedLocking.class){ //5、枷鎖 if(instance == null){ //6、第二次檢查 instance = new Instance(); //7、問題的根源在這里 } //8、 } } return instance; } }
感謝各位的閱讀,以上就是“java雙重檢查鎖問題怎么解決”的內容了,經過本文的學習后,相信大家對java雙重檢查鎖問題怎么解決這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。