設(shè)計模式之單例模式

單例設(shè)計模式理解起來非常簡單。一個類只允許創(chuàng)建一個對象(或者實例),那這個類就是一個單例類,這種設(shè)計模式就叫單例模式。

使用場景

處理資源訪問沖突

下面的示例中如果每個類都創(chuàng)建一個 Logger 實例,就可能造成日志內(nèi)容被覆蓋的情況。

public?class?Logger?{??private?FileWriter?writer;??public?Logger()?{????File?file?=?new?File("log.txt");????writer?=?new?FileWriter(file,?true);?//true表示追加寫入??}??public?void?log(String?message)?{????writer.write(mesasge);??}}public?class?UserController?{??private?Logger?logger?=?new?Logger();??public?void?login(String?username,?String?password)?{????//?...省略業(yè)務(wù)邏輯代碼...????logger.log(username?+?"?logined!");??}}public?class?OrderController?{??private?Logger?logger?=?new?Logger();??public?void?create(OrderVo?order)?{????//?...省略業(yè)務(wù)邏輯代碼...????logger.log("Created?an?order:?"?+?order.toString());??}}復(fù)制代碼

表示全局唯一類

如果有些數(shù)據(jù)在系統(tǒng)中只應(yīng)保存一份,那就比較適合設(shè)計為單例類。比如,配置信息類,全局 ID 生成器等。

如何實現(xiàn)一個單例?

要實現(xiàn)一個單例,我們要考慮以下幾點:

構(gòu)造函數(shù)需要是 private 訪問權(quán)限的,這樣才能避免外部通過 new 創(chuàng)建實例;

考慮對象創(chuàng)建時的線程安全問題;

考慮是否支持延遲加載;

考慮 getInstance() 性能是否高(是否加鎖)。

餓漢式

public?class?Singleton?{??private?static?final?Singleton?instance?=?new?Singleton();??private?Singleton()?{}??public?static?Singleton?getInstance()?{????return?instance;??}}復(fù)制代碼

懶漢式

懶漢式相對于餓漢式的優(yōu)勢是**「支持延遲加載」。但缺點也很明顯,因為使用了synchronized關(guān)鍵字導(dǎo)致這個方法的「并發(fā)度很低」**。如果這個單例類偶爾會被用到,那這種實現(xiàn)方式還可以接受。但是,如果頻繁地用到,就會導(dǎo)致性能瓶頸,這種實現(xiàn)方式就不可取了。

public?class?Singleton?{??private?static?Singleton?instance;??private?Singleton()?{}??public?static?synchronized?Singleton?getInstance()?{????if?(instance?==?null)?{??????instance?=?new?Singleton();????}????return?instance;??}}復(fù)制代碼

雙重檢測

這是一種既支持延遲加載、又支持高并發(fā)的單例實現(xiàn)方式。

public?class?Singleton?{??private?static?Singleton?instance;??private?Singleton()?{}??public?static?Singleton?getInstance()?{????if?(instance?==?null)?{??????synchronized(Singleton.class)?{?//?此處為類級別的鎖????????if?(instance?==?null)?{??????????instance?=?new?Singleton();????????}??????}????}????return?instance;??}}復(fù)制代碼

在 java1.5 以下instance = new Singleton();有指令重排問題,需要給instance成員變量加上volatile關(guān)鍵字,java1.5 之后不會再這個有問題。

靜態(tài)內(nèi)部類

這種方式利用了 Java 的靜態(tài)內(nèi)部類,有點類似餓漢式,但又能做到了延遲加載。

當(dāng)外部類 Singleton 被加載的時候,并不會創(chuàng)建 SingletonHolder 實例對象。只有當(dāng)調(diào)用 getInstance() 方法時,SingletonHolder 才會被加載,這個時候才會創(chuàng)建 instance。insance 的唯一性、創(chuàng)建過程的線程安全性,都由 JVM 來保證。所以,這種實現(xiàn)方法既保證了線程安全,又能做到延遲加載。

public?class?Singleton?{??private?Singleton()?{}??private?static?class?SingletonHolder{????private?static?final?Singleton?instance?=?new?Singleton();??}??public?static?Singleton?getInstance()?{????return?SingletonHolder.instance;??}}復(fù)制代碼

枚舉

這是一種最簡單的實現(xiàn)方式,基于枚舉類型的單例實現(xiàn)。這種實現(xiàn)方式通過 Java 枚舉類型本身的特性,保證了實例創(chuàng)建的線程安全性和實例的唯一性。

public?enum?IdGenerator?{??INSTANCE;??private?AtomicLong?id?=?new?AtomicLong(0);??public?long?getId()?{????return?id.incrementAndGet();??}}復(fù)制代碼

如何實現(xiàn)線程唯一的單例?

上面的單例類對象是進(jìn)程唯一的,一個進(jìn)程只能有一個單例對象。那如何實現(xiàn)一個線程唯一的單例呢?

假設(shè) IdGenerator 是一個線程唯一的單例類。在線程 A 內(nèi),我們可以創(chuàng)建一個單例對象 a。因為線程內(nèi)唯一,在線程 A 內(nèi)就不能再創(chuàng)建新的 IdGenerator 對象了,而線程間可以不唯一,所以,在另外一個線程 B 內(nèi),我們還可以重新創(chuàng)建一個新的單例對象 b。

我們通過一個 ConcurrentHashMap 來存儲對象,其中 key 是線程 ID,value 是對象。這樣我們就可以做到,不同的線程對應(yīng)不同的對象,同一個線程只能對應(yīng)一個對象。實際上,Java 語言本身提供了 ThreadLocal 工具類,可以更加輕松地實現(xiàn)線程唯一單例。

public?class?IdGenerator?{

??private?AtomicLong?id?=?new?AtomicLong(0);

??private?static?final?ConcurrentHashMap<Long,?IdGenerator>?instances

??????????=?new?ConcurrentHashMap<>();

??private?IdGenerator()?{}

??public?static?IdGenerator?getInstance()?{

????Long?currentThreadId?=?Thread.currentThread().getId();

????instances.putIfAbsent(currentThreadId,?new?IdGenerator());

????return?instances.get(currentThreadId);

??}

??public?long?getId()?{

????return?id.incrementAndGet();

??}

}

作者:開發(fā)者充電站

鏈接:https://juejin.cn/post/7053363491788292127

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容