public class ApiManager {
private static volatile ApiManager mInstance;
private ApiManager() {
}
public static ApiManager getInstance() {
if (mInstance == null) { // 判空1
synchronized (ApiManager.class) {
if (mInstance == null) { // 判空2
mInstance = new ApiManager();
}
}
}
return mInstance;
}
}
mInstance為什么要加volatile?
假設(shè)線程A執(zhí)行了mInstance = new ApiManager()竭贩,但沒(méi)有及時(shí)刷新到主內(nèi)存中,并且恰好在解鎖后,線程B剛好進(jìn)來(lái)執(zhí)行鎖區(qū)域內(nèi)的if (mInstance == null) 雨膨,由于線程A沒(méi)有把實(shí)例化后的mInstance刷新到主內(nèi)存中,所以此時(shí) mInstance == null為true读串,于是導(dǎo)致了多次實(shí)例化聊记。而加了volatile可以讓變量具備線程可見性撒妈,即保證:子線程讀取到volatile變量時(shí)會(huì)立刻從主內(nèi)存中取最新的值,子線程給volatile變量賦值后及時(shí)更新至主內(nèi)存排监。(詳情可參考《volatile及指令重排序》)
為什么要進(jìn)行兩次判空狰右?
1、假如沒(méi)有判空1舆床,只有判空2:
降低了多線程下的執(zhí)行效率棋蚌。比方說(shuō):mInstance不為空,且線程A和線程B同時(shí)需要執(zhí)行g(shù)etInstance()挨队,那么每次如果線程A持有鎖谷暮,那么線程B就得進(jìn)入阻塞狀態(tài),等線程A判空完畢后線程B才能繼續(xù)執(zhí)行盛垦,這不就降低了性能坷备。有了判空1,那么當(dāng)mInstance不為空時(shí)情臭,線程A和線程B只需一步判空并且不會(huì)發(fā)生阻塞省撑。
2、假如沒(méi)有判空2俯在,只有判空1:
多線程執(zhí)行g(shù)etInstance()有可能導(dǎo)致多次實(shí)例化竟秫。因?yàn)榧僭O(shè)線程A執(zhí)行了判空1后,線程B也開始執(zhí)行判空1跷乐,此時(shí)線程A和線程B都滿足了mInstance == null的條件肥败,即使線程A實(shí)例化mInstance,也無(wú)法影響線程B實(shí)例化mInstance愕提。