今天小編給大家分享一下java雙重檢查鎖問(wèn)題如何解決的相關(guān)知識(shí)點(diǎn),內(nèi)容詳細(xì),邏輯清晰,相信大部分人都還太了解這方面的知識(shí),所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來(lái)了解一下吧。
成都創(chuàng)新互聯(lián)成立與2013年,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目成都網(wǎng)站建設(shè)、成都做網(wǎng)站網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢(mèng)想脫穎而出為使命,1280元噶爾做網(wǎng)站,已為上家服務(wù),為噶爾各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話:13518219792
首先我們來(lái)看一下非線程安全的初始化單例模式
public class UnsafeLazyInitialization { private static UnsafeLazyInitialization instance; public static UnsafeLazyInitialization getInstance(){ if(instance == null){ //1: 線程A執(zhí)行 instance = new UnsafeLazyInitialization(); //2: 線程B執(zhí)行 } return instance; } }
在 UnsafeLazyInitialization 類中,假設(shè)線程A執(zhí)行到代碼1的時(shí)候,線程B執(zhí)行到代碼2, 這時(shí)候線程A 可能 看到 instance 引用對(duì)象還沒(méi)有完成初始化。
對(duì)于 UnsafeLazyInitialization 類,我們可以對(duì)getInstance()方法做同步處理來(lái)實(shí)現(xiàn)來(lái)實(shí)現(xiàn)線程安全的延遲初始化,示例代碼如下:
public static synchronized UnsafeLazyInitialization getInstance(){ if(instance == null){ //1: 線程A執(zhí)行 instance = new UnsafeLazyInitialization(); //2: 線程B執(zhí)行 } return instance; } }
由于上述代碼對(duì)getInstance()方法做了同步處理,這樣可能導(dǎo)致同步程序開銷加大。 如果getInstance()被多個(gè)線程頻繁調(diào)用,將會(huì)導(dǎo)致程序執(zhí)行性能降低,反之如果不是被多個(gè)線程調(diào)用,那個(gè)這個(gè)getInstance()方法的延遲初始化方法將影響性能。
JVM 1.6之前 synchronized是重量級(jí)鎖,所以很耗費(fèi)性能,所以人們想到了一個(gè)種雙重校驗(yàn)鎖(Dobule-check Locking)的方案來(lái)提高性能,示例代碼如下:
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、問(wèn)題的根源在這里 } //8、 } } return instance; } }
如上代碼所示:如果 步驟4、第一次檢查instance不為null,則就不需要執(zhí)行下面的加鎖操作,大大降低了synchronized 鎖帶來(lái)的性能問(wèn)題。上面代碼看起來(lái)沒(méi)有任何問(wèn)題。 1、多個(gè)線程視圖去創(chuàng)建新對(duì)象的時(shí)候,通過(guò)synchronized關(guān)鍵字可以保證只有一個(gè)線程創(chuàng)建對(duì)象成功。
2、如果instance 實(shí)例對(duì)象已經(jīng)被創(chuàng)建,則直接通過(guò)getInstatnce()方法獲取對(duì)象實(shí)例。
上面代碼看上去很完美,但是當(dāng)執(zhí)行步驟4的時(shí)候,instatnce!=null 的時(shí)候,instatnce 的引用對(duì)象有可能還沒(méi)有完成初始化。
上面代碼我們執(zhí)行到步驟7的時(shí)候,instance = new Instance(); ,創(chuàng)建了一個(gè)對(duì)象,這個(gè)創(chuàng)建對(duì)象的步驟可以分為三步,如下:
memory = allocate() //1.分配內(nèi)存空間memory ctorInstance(memory) //2, 初始化對(duì)象在內(nèi)存 分配內(nèi)存空間memory上初始化 Singleton 對(duì)象 instance = memory //3、設(shè)置 instance 指向剛分配的內(nèi)存地址memory
上面三行代碼 2和3可能發(fā)生重排序,在(JTI編譯器上,這種重排序是真是發(fā)生的) 步驟2和步驟3發(fā)生重排序后執(zhí)行順序
memory = allocate() //1.分配內(nèi)存空間memory instance = memory //3、設(shè)置 instance 指向剛分配的內(nèi)存地址memory // 注意此時(shí)instance對(duì)象還沒(méi)有被初始化,但是instance的引用已經(jīng)不是null了。 ctorInstance(memory) //2, 初始化對(duì)象在內(nèi)存 分配內(nèi)存空間memory上初始化 Singleton 對(duì)象
下面看一下多線程執(zhí)行順序
上述代碼第7行instance = new Instance(); 如果A線程發(fā)生指令重排序(2,3),那么另一個(gè)線程B有可能在4行代碼判斷 instance 不為空。線程B接下來(lái)訪問(wèn)instance的引用對(duì)象,但是instance對(duì)象有可能還沒(méi)被A初始化完成。此時(shí)線程B可能訪問(wèn)一個(gè)沒(méi)有初始化完成的對(duì)象,導(dǎo)致報(bào)空指針錯(cuò)誤。
1、不允許2、3進(jìn)行指令重排。 2、允許2、3進(jìn)行重排序,但是不允許其它線程看到重排序
基于上面代碼只需要在instance聲明時(shí)加上volatile關(guān)鍵字就可以,如下代碼
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、問(wèn)題的根源在這里 } //8、 } } return instance; } }
以上就是“java雙重檢查鎖問(wèn)題如何解決”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會(huì)為大家更新不同的知識(shí),如果還想學(xué)習(xí)更多的知識(shí),請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。