JAVA實現(xiàn)單例模式的四種方法和一些特點
一、餓漢式單例類
public class Singleton
{
private Singleton(){
}
private static Singleton instance = new Singleton();
private static Singleton getInstance(){
return instance;
}
}
特點:餓漢式提前實例化,沒有懶漢式中多線程問題,但不管我們是不是調(diào)用getInstance()都會存在一個實例在內(nèi)存中
二、內(nèi)部類式單例類
public class Singleton
{
private Singleton(){
}
private class SingletonHoledr(){
private static Singleton instance = new Singleton();
}
private static Singleton getInstance(){
return SingletonHoledr.instance;
}
}
特點:內(nèi)部類式中,實現(xiàn)了延遲加載,只有我們調(diào)用了getInstance(),才會創(chuàng)建唯一的實例到內(nèi)存中.并且也解決了懶漢式中多線程的問題.解決的方式是利用了Classloader的特性.
三、懶漢式單例類
public class Singleton
{
private Singleton(){
}
private static Singleton instance;
public static Singleton getInstance(){
if(instance == null){
return instance = new Singleton();
}else{
return instance;
}
}
}
特點:在懶漢式中,有線程A和B,當(dāng)線程A運(yùn)行到第8行時,跳到線程B,當(dāng)B也運(yùn)行到8行時,兩個線程的instance都為空,這樣就會生成兩個實例。解決的辦法是同步:
可以同步但是效率不高:
public class Singleton
{
private Singleton(){
}
private static Singleton instance;
public static synchronized Singleton getInstance(){
if(instance == null){
return instance = new Singleton();
}else{
return instance;
}
}
}
這樣寫程序不會出錯,因為整個getInstance是一個整體的"critical section",但就是效率很不好,因為我們的目的其實只是在第一個初始化instance的時候需要locking(加鎖),而后面取用instance的時候,根本不需要線程同步。
于是聰明的人們想出了下面的做法:
雙檢鎖寫法:
public class Singleton{
private static Singleton single; //聲明靜態(tài)的單例對象的變量
private Singleton(){} //私有構(gòu)造方法
public static Singleton getSingle(){ //外部通過此方法可以獲取對象
if(single == null){
synchronized (Singleton.class) { //保證了同一時間只能只能有一個對象訪問此同步塊
if(single == null){
single = new Singleton();
}
}
}
return single; //返回創(chuàng)建好的對象
}
}
思路很簡單,就是我們只需要同步(synchronize)初始化instance的那部分代碼從而使代碼既正確又很有效率。
這就是所謂的“雙檢鎖”機(jī)制(顧名思義)。
很可惜,這樣的寫法在很多平臺和優(yōu)化編譯器上是錯誤的。
原因在于:instance = new Singleton()這行代碼在不同編譯器上的行為是無法預(yù)知的。一個優(yōu)化編譯器可以合法地如下實現(xiàn)instance = new Singleton():
1. instance = 給新的實體分配內(nèi)存
2. 調(diào)用Singleton的構(gòu)造函數(shù)來初始化instance的成員變量
現(xiàn)在想象一下有線程A和B在調(diào)用getInstance,線程A先進(jìn)入,在執(zhí)行到步驟1的時候被踢出了cpu。然后線程B進(jìn)入,B看到的是instance 已經(jīng)不是null了(內(nèi)存已經(jīng)分配),于是它開始放心地使用instance,但這個是錯誤的,因為在這一時刻,instance的成員變量還都是缺省值,A還沒有來得及執(zhí)行步驟2來完成instance的初始化。
當(dāng)然編譯器也可以這樣實現(xiàn):
1. temp = 分配內(nèi)存
2. 調(diào)用temp的構(gòu)造函數(shù)
3. instance = temp
如果編譯器的行為是這樣的話我們似乎就沒有問題了,但事實卻不是那么簡單,因為我們無法知道某個編譯器具體是怎么做的,因為在Java的memory model里對這個問題沒有定義。
雙檢鎖對于基礎(chǔ)類型(比如int)適用。很顯然吧,因為基礎(chǔ)類型沒有調(diào)用構(gòu)造函數(shù)這一步。
相關(guān)文章
SpringBoot調(diào)用第三方WebService接口的兩種方法
本文主要介紹了SpringBoot調(diào)用第三方WebService接口的兩種方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06SpringCloud手寫Ribbon實現(xiàn)負(fù)載均衡
這篇文章主要介紹了SpringCloud手寫Ribbon實現(xiàn)負(fù)載均衡的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01SpringCloud?Feign集成AOP的常見問題與解決
在使用?Spring?Cloud?Feign?作為微服務(wù)通信的工具時,我們可能會遇到?AOP?不生效的問題,這篇文章將深入探討這一問題,給出幾種常見的場景,分析可能的原因,并提供解決方案,希望對大家有所幫助2023-10-10解決spring 處理request.getInputStream()輸入流只能讀取一次問題
這篇文章主要介紹了解決spring 處理request.getInputStream()輸入流只能讀取一次問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09