今天小編給大家分享一下Java單例模式的餓漢和懶漢模式怎么實現(xiàn)的相關(guān)知識點,內(nèi)容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
讓客戶滿意是我們工作的目標,不斷超越客戶的期望值來自于我們對這個行業(yè)的熱愛。我們立志把好的技術(shù)通過有效、簡單的方式提供給客戶,將通過不懈努力成為客戶在信息化領(lǐng)域值得信任、有價值的長期合作伙伴,公司提供的服務(wù)項目有:域名注冊、網(wǎng)站空間、營銷軟件、網(wǎng)站建設(shè)、武清網(wǎng)站維護、網(wǎng)站推廣。
保證某個類在程序中只存在一份實例,而不會創(chuàng)建多個實例,這樣就會提高效率。
在單利模式中一般只提供一個getInstance()方法來獲取實例對象,不提供setInstance()方法,目的是為了避免再實例化出其他實例對象。其中單例模式中有兩種模式一種是餓漢模式,一種是懶漢模式。
餓漢模式就是在類加載的時候立刻會實例化,后續(xù)使用就只會出現(xiàn)一份實例。
package thread.example; //餓漢模式 public class HungrySingle { //在類加載的時候就實例化了,類加載只有一次,所以值實例化出了一份該實例對象 private static HungrySingle instance = new HungrySingle(); public static HungrySingle getInstance() { return instance; } }
在類加載的時候就已經(jīng)實例化了,所以該實例化沒有涉及到實例化的修改操作,只是進行讀取操作。在多線程情況下是線程安全的。
在類加載的時候沒有直接實例化,而是調(diào)用指定實例方法的時候再進行實例化,這樣就能保證不想使用的時候也不會實例化。一般來說比餓漢模式的效率高。
package thread.example; //單線程的懶漢模式 public class LazySingle { private static LazySingle instance = null; //只有在調(diào)用該方法的時候才實例化 public static LazySingle getInstance() { if(instance == null) { instance = new LazySingle(); } return instance; } }
(1)導(dǎo)致懶漢模式在多線程情況下的不安全原因
在多線程的情況下,由于可能兩個線程都會得到一份instance=null,這是因為如果線程1修改了自己縣城中的instance后還沒來得及修改主內(nèi)存中的instance,所導(dǎo)致線程2也實例化出了一份instance對象,這時候也就不再是單例模式了。主要導(dǎo)致該問題的是由于這里面涉及到了對instance的修改操作,失去了原子性,為了保證原子性,我們想到了加鎖,從而實現(xiàn)線程安全問題。
(2)解決方法代碼示例
版本1
package thread.example; //多線程安全下的懶漢模式 public class LazySingle { private LazySingle() { } private static LazySingle instance = null; //只有在調(diào)用該方法的時候才實例化 public static synchronized LazySingle getInstance() { if (instance == null) { instance = new LazySingle(); } return instance; } }
版本1的代碼雖然保證了線程安全,但是每次調(diào)用該方法時還是會出現(xiàn)加鎖解鎖問題,為了進一步優(yōu)化,我們可以減小鎖的粒度來提高效率,因為加了鎖之后也就和高并發(fā)無緣了,但我們還是想提高效率,所以才會進行優(yōu)化。
版本2
雙重if判斷加鎖提高效率
package thread.example; public class SecurityLazyModle { private LazySingle() { } private static volatile SecurityLazyModle instance = null;//保證內(nèi)存可見性,防止編譯器過度優(yōu)化(指令重排序) public static SecurityLazyModle getInstance() { if(instance == null) { synchronized (SecurityLazyModle.class) { if(instance == null) { instance = new SecurityLazyModle(); } } } return instance; } }
版本2的解釋說明
第一層if是為了判斷當前是否已經(jīng)把實例創(chuàng)建出來,第二層synchronized是為了使進入當前if中的線程來競爭鎖,當拿到鎖的線程進入到第三層if之后判斷是否為空,不為空就是實例化對象,然后再釋放鎖,釋放鎖之后,instance已經(jīng)不為空了,后面的線程就被阻擋在了第三層if這里了,之后再來訪問getInstance()方法,發(fā)現(xiàn)該instance已經(jīng)不為空了,也就不用再搶占鎖資源了,因為競爭鎖也消耗大量的時間。通過這樣處理,既保證了線程安全,也提高了效率。
這里使用volatile是為了防止編譯器優(yōu)化導(dǎo)致的指令重排序,在進行new一個對象不是原子性操作,可以分為三步驟:
1.分配內(nèi)存空間
2.實例化對象
3.給變量賦值
對于上面的執(zhí)行,如果1和3先執(zhí)行了(假設(shè)2還沒有完成),在第一層if外的線程這時候判斷不為null,這時候就會直接返回該對象,但是這個對象只執(zhí)行了一半,之后使用就會導(dǎo)致線程安全問題。
通過volatile就可以確保這3步驟必須執(zhí)行完(無論順序如何,最終都會執(zhí)行完),外面的線程才可以執(zhí)行,這時候就保證了該對象的完整性。
以上就是“Java單例模式的餓漢和懶漢模式怎么實現(xiàn)”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學(xué)習(xí)更多的知識,請關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。