您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(xì)講解有關(guān)在java中使用單例模式時(shí)需要注意哪些事項(xiàng),文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。
單例模式的兩種方法:懶漢式和餓漢式
首先說(shuō)說(shuō)類什么時(shí)候進(jìn)行加載?
java虛擬機(jī)沒(méi)有進(jìn)行強(qiáng)制性的約束,但是對(duì)于初始化卻嚴(yán)格規(guī)定了有且只有4種情況必須先對(duì)類進(jìn)行初始化。
我們要知道的是在類加載的過(guò)程中,加載、驗(yàn)證、準(zhǔn)備是在初始化之前完成的,所以進(jìn)行了初始化,加載、驗(yàn)證、準(zhǔn)備自然就在之前完成了。
然后這四種情況是分別遇到 new 、 getstatic 、 putstatic 和 invokestatic 這四條指令時(shí),如果對(duì)應(yīng)的類沒(méi)有初始化,則要對(duì)對(duì)應(yīng)的類先進(jìn)行初始化。
講完類加載時(shí)機(jī),就可以講懶漢式和餓漢式了。
先看最開(kāi)始的代碼:
public class Student2 { //1:構(gòu)造私有 private Student2(){} //2:定義私有靜態(tài)成員變量,先不初始化 private static Student2 student = null; //3:定義公開(kāi)靜態(tài)方法,獲取本身對(duì)象 public static Student2 getSingletonInstance(){ //沒(méi)有對(duì)象,再去創(chuàng)建 if (student == null) { student = new Student2(); } //有對(duì)象就返回已有對(duì)象 return student; } }
結(jié)合之前講的類加載內(nèi)容,遇到new或加載靜態(tài)方法了就會(huì)進(jìn)行類加載了。
線程1它new了一個(gè)對(duì)象,線程2它緊接著也new一個(gè)對(duì)象,第二個(gè)對(duì)象的值把第一個(gè)對(duì)象的值覆蓋了,不管new了多少個(gè)對(duì)象,都會(huì)產(chǎn)生垃圾對(duì)象,只有最后一個(gè)對(duì)象才會(huì)保持住,其他對(duì)象都會(huì)變成不可達(dá)對(duì)象,被垃圾回收,這個(gè)過(guò)程就相當(dāng)于產(chǎn)生了大量無(wú)效對(duì)象,這就是線程不安全的原因!
看代碼:
public class Student4 { private volatile static Student4 student = null; private Student4() {} public static Student4 getSingletonInstance() { if (student == null) {//第一個(gè)null判斷,是先大范圍過(guò)濾一遍 synchronized (Student4.class) { if (student == null) { student = new Student4(); } } } return student; } }
這個(gè)叫雙重檢查鎖DCL,第一個(gè)if先大范圍判斷是不是空值,經(jīng)過(guò)synchronized,線程1先進(jìn)去執(zhí)行完后,線程2才能進(jìn)去,然后第二個(gè)if判斷是否完成創(chuàng)建類的實(shí)例,線程1創(chuàng)建完了,線程2就不用創(chuàng)建了。
因?yàn)槲覀僑tudent student = new Student()的執(zhí)行過(guò)程是:
1、new觸發(fā)類加載機(jī)制(已經(jīng)被加載過(guò)的類不需要再次加載)
2、分配內(nèi)存空間
3、將對(duì)象進(jìn)行初始化4、講對(duì)象引用地址賦值給棧空間中的變量但我們JVM中的JIT即時(shí)編輯器會(huì)對(duì)代碼的執(zhí)行過(guò)程進(jìn)行優(yōu)化,把過(guò)程變?yōu)?、2、4、3。
這是什么意思呢?就是未經(jīng)初始化直接賦值,這樣就是student直接有值了,但整個(gè)對(duì)象還未初始化完成,所以這個(gè)對(duì)象是不完整的,是個(gè)未成品。在JVM規(guī)范中,它是一個(gè)根本不能用的對(duì)象。
到了這個(gè)時(shí)候,線程1做了這么多事,我們讓它休息會(huì),給CPU稍微停一下,線程2就來(lái)了,它就直接得到了對(duì)象,但它調(diào)用對(duì)象的方法時(shí),就會(huì)報(bào)錯(cuò)。雖然這個(gè)對(duì)象有值,但還未初始化完成。所以我們要加上volatile關(guān)鍵字禁止指令重新排序。
面試重災(zāi)區(qū)說(shuō)的差不多了,餓漢式還是要講講。
看代碼:
public class Student1 { // 2:成員變量初始化本身對(duì)象 private static Student1 student = new Student1(); // 構(gòu)造私有 private Student1() { } // 3:對(duì)外提供公共方法獲取對(duì)象 public static Student1 getSingletonInstance() { return student; } public void sayHello(String name) { System.out.println("hello," + name); } }
根據(jù)類加載的東西,在多線程的條件下,線程1先執(zhí)行g(shù)etSingletonInstance()時(shí),就會(huì)進(jìn)行類加載,類的靜態(tài)資源就會(huì)進(jìn)行初始化。根據(jù)JVM安全機(jī)制里說(shuō)的,當(dāng)一個(gè)類被JVM加載的時(shí)候,該類的加載是線程安全的,相當(dāng)于JVM對(duì)該過(guò)程加鎖了。所以整個(gè)過(guò)程處于一個(gè)鎖的范圍內(nèi),然后靜態(tài)成員變量進(jìn)行初始化就相當(dāng)于Student1()被new了,只會(huì)被new一次。
當(dāng)?shù)诙€(gè)線程進(jìn)來(lái),它就發(fā)現(xiàn)這個(gè)類已經(jīng)被加載了,就不需要進(jìn)行加載了,對(duì)象也不需要頻繁創(chuàng)建,所以線程是安全的!
關(guān)于在java中使用單例模式時(shí)需要注意哪些事項(xiàng)就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。