鍍金池/ 問答/Java  網(wǎng)絡(luò)安全/ 面試時(shí)手寫按需加載、多線程環(huán)境下的單例類,面試官非說得加個(gè)volatile

面試時(shí)手寫按需加載、多線程環(huán)境下的單例類,面試官非說得加個(gè)volatile

上周去面試,手寫單例類

然后我手寫一個(gè)平時(shí)用的比較多的全局設(shè)置類:


import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Setting {

    private static Setting setting;

    private  static Lock lock = new ReentrantLock();

    private Setting(){

    }

    public static Setting get() {
        if(setting != null) {
            return  setting;
        }

        lock.lock();
        if(setting == null) {
            setting = new Setting();
        }
        lock.unlock();

        return setting;
    }
}

結(jié)果面試官非說這單例在JVM指令重排序的情況下會(huì)出問題,非說必須為setting對(duì)象加一個(gè)volatile,當(dāng)時(shí)很懵逼,現(xiàn)在也很懵逼。本人小白,各位大神分析一下?

回答
編輯回答
忠妾
2017年8月29日 16:08
編輯回答
浪蕩不羈

指令重排序是為了優(yōu)化指令,提高程序運(yùn)行效率。
比如instance = new Singleton() 可分解為:

1、memory = allocate();   // 開辟內(nèi)存空間
2、ctorInstance(memory);  // 構(gòu)造對(duì)象  
3、instance = memory;     // 將instance指向內(nèi)存空間

在第三行代碼不依賴第二行代碼的情況下,JVM會(huì)發(fā)成重排序:

1、memory = allocate();   // 開辟內(nèi)存空間
2、instance = memory;     // 將instance指向內(nèi)存空間
3、ctorInstance(memory);  // 構(gòu)造對(duì)象  

重排序之后的代碼,在單線程情況下執(zhí)行沒什么影響,多線程的話執(zhí)行順序如下:
線程A:

lock.lock();
if(setting == null) {
    //當(dāng)A線程執(zhí)行到上述重排序之后的instance = memory;這句時(shí),setting已經(jīng)不為空,此時(shí)線程B進(jìn)入
    setting = new Setting(); 
}

線程B:

if(setting != null) {
    // 會(huì)直接返回沒有執(zhí)行ctorInstance(memory);的實(shí)例,即還沒有被完全初始化的實(shí)例。
    return  setting; 
}

解決方法:
private static Setting setting;改為 private static volatile Setting setting;
volatile是jdk1.5之后加入的特性,可以禁止變量使用重排序。
碼字不易,有用望采納

2017年10月16日 15:29