單例模式可以說是23中設計模式中應用最廣的一種模式了。
定義:確保某一個類只有一個實例,自行實例化並且想整個系統提供這個實例。
使用場景:避免某個類產生多個對象而消耗過多的資源,確保某個類在程序中只有一個實例。比如我們使用的圖片加載器ImageLoader。往往單例創建的對象,耗費的資源都比較多,所以在初始化單例對象的時候就顯得尤爲重要了,接下來,我們就來聊一聊單例的幾種實現方式。
一、餓漢式
[java] view plain copy
public class ImageLoader{
private static ImageLoader instance = new ImageLoader;
private ImageLoader(){}
public static ImageLoader getInstance(){
return instance;
}
}
餓漢式顧名思義,就是這個漢子很餓,一上來就把單例對象創建出來了,要用的時候直接返回即可,這種可以說是單例模式中最簡單的一種實現方式。但是問題也比較明顯。單例在還沒有使用到的時候,初始化就已經完成了。也就是說,如果程序從頭到位都沒用使用這個單例的話,單例的對象還是會創建。這就造成了不必要的資源浪費。所以不推薦這種實現方式。
二、懶漢式
[java] view plain copy
public class ImageLoader{
private static ImageLoader instance;
private ImageLoader(){}
public static synchronized ImageLoader getInstance(){
if(instance == null){
instance = new ImageLoader();
}
return instance;
}
}
餓漢式也顧名思義,就是這個漢子比較懶,一開始的時候什麼也不做,知道要使用的時候採取創建實例的對象。看起來還不錯,只有在使用實例的時候,我們纔回去創建對象。但是細心的同學可能發現了,我們在獲取實例的方法上加了鎖,避免多線程引發的創建多個單例的情況。多線程的問題是避免了,但也造成了整體性能的下降,每次使用單例對象,都需要鎖判斷,降低了整體性能。很明顯,懶漢式也不是我們所要追求的目標。
三、Double CheckLock實現單例
英文稍好點的東西,應該都看懂了,DCL也就是雙重鎖判斷機制,直接上代碼。
[java] view plain copy
public class ImageLoader{
private static ImageLoader instance;
private ImageLoader(){}
public static ImageLoader getInstance(){
if(instance == null){
synchronized (ImageLoader.class){
if(instance == null){
instance = new ImageLoader();
}
}
}
return instance;
}
}
可以看到,在獲取單例對象的時候,我們先進行了兩爲空判斷,並且在第二次判斷前加了鎖,這就讓程序變得更加優秀,在使用的時候,只會前幾次獲取單例對象的時候會進行鎖判斷,一旦單例對象創建完成,鎖的任務也就完成了,在懶漢式的基礎上,提高了性能。DCL是使用最多的單例實現方式,能夠在使用的時候才進行單例對象的初始化創建,並且能夠在絕大多數情況下保證對象的唯一性的正確性。請注意,是絕大多數情況下,也就是說,這種模式也不能完全保證單例的對象的完美實現,但是,就一般情況下,這種模式都能滿足需求。俗話說,學無止境,接下來我們就來看看單例模式的終極實現版本。
四、靜態內部類實現模式
直接上代碼
[java] view plain copy
public class ImageLoader{
private static class InnerInstance{
private static final ImageLoader instance = new ImageLoader();
}
private ImageLoader(){}
public static ImageLoader ImageLoader(){
return InnerInstance.instance;
}
}
可以發現這種方式,並未加鎖,因爲第一次加載ImageLoader類時,並不會實例化單例對象,只有第一次調用getInstance()方法時會導致虛擬機加載InnerInstance類,這種
方式不僅能保證對象的單一性,還避免加鎖帶來的性能問題,又啓動了延遲加載的優化,所以這就是單例模式的終極實現版本,也是推薦使用的方式。