摘要:在寫單例模式的代碼之前,我們先簡單了解一下兩個知識點,關于類的加載順序和關鍵字。懶漢和餓漢在程序編寫上,一般將單例模式分為兩種,分別是餓漢式和懶漢式,餓漢式在類加載時就完成了初始化,所以類加載比較慢,但獲取對象的速度快。
定義單例模式是比較常見的一種設計模式,目的是保證一個類只能有一個實例,而且自行實例化并向整個系統(tǒng)提供這個實例,避免頻繁創(chuàng)建對象,節(jié)約內存。
單例模式的應用場景很多,
比如我們電腦的操作系統(tǒng)的回收站就是一個很好的單例模式應用,電腦上的文件、視頻、音樂等被刪除后都會進入到回收站中;還有計算機中的打印機也是采用單例模式設計的,一個系統(tǒng)中可以存在多個打印任務,但是只能有一個正在工作的任務;Web頁面的計數(shù)器也是用單例模式實現(xiàn)的,可以不用把每次刷新都記錄到數(shù)據(jù)庫中。
通過回味這些應用場景,我們對單例模式的核心思想也就有了更清晰的認識,下面就開始用代碼來實現(xiàn)。
在寫單例模式的代碼之前,我們先簡單了解一下兩個知識點,關于類的加載順序和static關鍵字。
類加載順序類加載(classLoader)機制一般遵從下面的加載順序
如果類還沒有被加載:
先執(zhí)行父類的靜態(tài)代碼塊和靜態(tài)變量初始化,靜態(tài)代碼塊和靜態(tài)變量的執(zhí)行順序跟代碼中出現(xiàn)的順序有關。
執(zhí)行子類的靜態(tài)代碼塊和靜態(tài)變量初始化。
執(zhí)行父類的實例變量初始化
執(zhí)行父類的構造函數(shù)
執(zhí)行子類的實例變量初始化
執(zhí)行子類的構造函數(shù)
同時,加載類的過程是線程私有的,別的線程無法進入。
如果類已經(jīng)被加載:
靜態(tài)代碼塊和靜態(tài)變量不在重復執(zhí)行,再創(chuàng)建類對象時,只執(zhí)行與實例相關的變量初始化和構造方法。
static關鍵字一個類中如果有成員變量或者方法被static關鍵字修飾,那么該成員變量或方法將獨立于該類的任何對象。它不依賴類特定的實例,被類的所有實例共享,只要這個類被加載,該成員變量或方法就可以通過類名去進行訪問,它的作用用一句話來描述就是,不用創(chuàng)建對象就可以調用方法或者變量,這簡直就是為單例模式的代碼實現(xiàn)量身打造的。
下面將列舉幾種單例模式的實現(xiàn)方式,其關鍵方法都是用static修飾的,并且,為了避免單例的類被頻繁創(chuàng)建對象,我們可以用private的構造函數(shù)來確保單例類無法被外部實例化。
懶漢和餓漢在程序編寫上,一般將單例模式分為兩種,分別是餓漢式和懶漢式,
餓漢式:在類加載時就完成了初始化,所以類加載比較慢,但獲取對象的速度快。
懶漢式:在類加載時不初始化,等到第一次被使用時才初始化。
代碼實現(xiàn)1、餓漢式 (可用)
public class Singleton {
private final static Singleton INSTANCE = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return INSTANCE;
}
}
這是比較常見的寫法,在類加載的時候就完成了實例化,避免了多線程的同步問題。當然缺點也是有的,因為類加載時就實例化了,沒有達到Lazy Loading (懶加載) 的效果,如果該實例沒被使用,內存就浪費了。
2、普通的懶漢式 (線程不安全,不可用)
public class Singleton {
private static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
這是懶漢式中最簡單的一種寫法,只有在方法第一次被訪問時才會實例化,達到了懶加載的效果。但是這種寫法有個致命的問題,就是多線程的安全問題。假設對象還沒被實例化,然后有兩個線程同時訪問,那么就可能出現(xiàn)多次實例化的結果,所以這種寫法不可采用。
3、同步方法的懶漢式 (可用)
public class Singleton {
private static Singleton instance = null;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
這種寫法是對getInstance()加了鎖的處理,保證了同一時刻只能有一個線程訪問并獲得實例,但是缺點也很明顯,因為synchronized是修飾整個方法,每個線程訪問都要進行同步,而其實這個方法只執(zhí)行一次實例化代碼就夠了,每次都同步方法顯然效率低下,為了改進這種寫法,就有了下面的雙重檢查懶漢式。
4、雙重檢查懶漢式 (可用,推薦)
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
這種寫法用了兩個if判斷,也就是Double-Check,并且同步的不是方法,而是代碼塊,效率較高,是對第三種寫法的改進。為什么要做兩次判斷呢?這是為了線程安全考慮,還是那個場景,對象還沒實例化,兩個線程A和B同時訪問靜態(tài)方法并同時運行到第一個if判斷語句,這時線程A先進入同步代碼塊中實例化對象,結束之后線程B也進入同步代碼塊,如果沒有第二個if判斷語句,那么線程B也同樣會執(zhí)行實例化對象的操作了。
5、靜態(tài)內部類 (可用,推薦)
public class Singleton {
private Singleton() {}
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
這是很多開發(fā)者推薦的一種寫法,這種靜態(tài)內部類方式在Singleton類被裝載時并不會立即實例化,而是在需要實例化時,調用getInstance方法,才會裝載SingletonInstance類,從而完成對象的實例化。
同時,因為類的靜態(tài)屬性只會在第一次加載類的時候初始化,也就保證了SingletonInstance中的對象只會被實例化一次,并且這個過程也是線程安全的。
6、枚舉 (可用、推薦)
public enum Singleton { INSTANCE; }
這種寫法在《Effective JAVA》中大為推崇,它可以解決兩個問題:
1)線程安全問題。因為Java虛擬機在加載枚舉類的時候會使用ClassLoader的方法,這個方法使用了同步代碼塊來保證線程安全。
2)避免反序列化破壞對象,因為枚舉的反序列化并不通過反射實現(xiàn)。
好了,單例模式的幾種寫法就介紹到這了,最后簡單總結一下單例模式的優(yōu)缺點
單例模式的優(yōu)缺點單例類只有一個實例,節(jié)省了內存資源,對于一些需要頻繁創(chuàng)建銷毀的對象,使用單例模式可以提高系統(tǒng)性能;
單例模式可以在系統(tǒng)設置全局的訪問點,優(yōu)化和共享數(shù)據(jù),例如前面說的Web應用的頁面計數(shù)器就可以用單例模式實現(xiàn)計數(shù)值的保存。
單例模式一般沒有接口,擴展的話除了修改代碼基本上沒有其他途徑。
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://systransis.cn/yun/7190.html
摘要:那有什么辦法保證只有一個領導人斯大林呢較常見的兩種方式餓漢式和懶漢式二實戰(zhàn)圖這里提示一點,在學習設計模式的時候,圖會讓你更容易,而且深刻的去理解到該模式的核心。下一篇的設計模式是工廠方法模式。 ??就算不懂設計模式的兄弟姐妹們,想必也聽說過單例模式,并且在項目中也會用上。但是,真正理解和熟悉單例模式的人有幾個呢?接下來我們一起來學習設計模式中最簡單的模式之一——單例模式 一、為什么叫單...
摘要:總之,選擇單例模式就是為了避免不一致狀態(tài),避免政出多頭。二餓漢式單例餓漢式單例類在類初始化時,已經(jīng)自行實例化靜態(tài)工廠方法餓漢式在類創(chuàng)建的同時就已經(jīng)創(chuàng)建好一個靜態(tài)的對象供系統(tǒng)使用,以后不再改變,所以天生是線程安全的。 概念: Java中單例模式是一種常見的設計模式,單例模式的寫法有好幾種,這里主要介紹兩種:懶漢式單例、餓漢式單例。 單例模式有以下特點: 1、單例類只能有一個實例。 ...
摘要:如此便可使得這一實現(xiàn)方式能夠同時具備線程安全延遲加載以及節(jié)省大量同步判斷資源等優(yōu)勢,可以說是單例模式的最佳實現(xiàn)了 單例模式(Singleton)是一種使用率非常高的設計模式,其主要目的在于保證某一類在運行期間僅被創(chuàng)建一個實例,并為該實例提供了一個全局訪問方法,通常命名為getInstance()方法。單例模式的本質簡言之即是: 控制實例數(shù)目 以Java為例,單例模式通??煞譃轲I漢式和懶...
閱讀 3012·2021-09-23 11:32
閱讀 2967·2021-09-22 15:12
閱讀 1738·2019-08-30 14:07
閱讀 3489·2019-08-29 16:59
閱讀 1677·2019-08-29 11:11
閱讀 2346·2019-08-26 13:50
閱讀 2454·2019-08-26 13:49
閱讀 2649·2019-08-26 11:49