一、設(shè)計(jì)模式
設(shè)計(jì)模式是一種在軟件設(shè)計(jì)中經(jīng)過驗(yàn)證的解決問題的方案或者模版。它們是從實(shí)踐中總結(jié)出來的,可以幫助解決常見的設(shè)計(jì)問題,提高代碼的重用性、維護(hù)性和擴(kuò)展性。
設(shè)計(jì)模式可以分為三大類:
-
創(chuàng)建型模式(Creational Patterns):創(chuàng)建型模式關(guān)注對(duì)象的實(shí)例化過程,包括如何創(chuàng)建、組合和表示對(duì)象。常見的創(chuàng)建型模式有單例模式、工廠模式、抽象工廠模式、建造者模式和原型模式等。
-
結(jié)構(gòu)型模式(Structural Patterns):結(jié)構(gòu)型模式關(guān)注對(duì)象的組合和關(guān)系,以實(shí)現(xiàn)更大的結(jié)構(gòu)和功能。常見的結(jié)構(gòu)型模式有適配器模式、裝飾器模式、代理模式、組合模式、享元模式和橋接模式等。
-
行為型模式(Behavioral Patterns):行為型模式關(guān)注對(duì)象之間的通信和協(xié)作,以實(shí)現(xiàn)特定的行為和交互模式。常見的行為型模式有觀察者模式、策略模式、模板方法模式、迭代器模式、狀態(tài)模式和命令模式等。
每種設(shè)計(jì)模式都有其特定的應(yīng)用場景和優(yōu)勢,可以根據(jù)需求選擇適當(dāng)?shù)哪J?。設(shè)計(jì)模式可以提供一些常用的解決方案,幫助開發(fā)人員更好地組織代碼結(jié)構(gòu)、降低耦合度、增加靈活性和可維護(hù)性。
二、單例模式
2.1 單例模式的概念與特點(diǎn)
單例模式(Singleton Pattern)是一種創(chuàng)建型設(shè)計(jì)模式,用于確保一個(gè)類只存在一個(gè)實(shí)例,并提供全局訪問點(diǎn)來訪問該實(shí)例。
在軟件開發(fā)中,有些類只需要擁有一個(gè)全局實(shí)例,例如日志記錄器、數(shù)據(jù)庫連接池、線程池等。使用單例模式可以確保這些類只被實(shí)例化一次,從而節(jié)省系統(tǒng)資源,避免不必要的對(duì)象的創(chuàng)建和銷毀。
單例模式通常具有以下特點(diǎn):
- 私有構(gòu)造方法:單例類的構(gòu)造方法是私有的,這樣做的目的是為了防止外部代碼直接實(shí)例化該類對(duì)象。
-
靜態(tài)訪問方法:單例模式提供一個(gè)靜態(tài)方法,用于獲取單例實(shí)例。這個(gè)方法通常被命名為
getInstance()
。 - 線程安全性:單例模式需要考慮多線程環(huán)境下的線程安全問題。確保在多線程環(huán)境下獲取單例實(shí)例的方法是線程安全的,可以使用同步機(jī)制和雙重檢查鎖(Double-Checked Locking)等方式實(shí)現(xiàn)。
- 全局訪問:單例模式提供單例實(shí)例的全局訪問,其他代碼可以通過調(diào)用單例類的訪問方法來獲取實(shí)例。
單例模式可以帶來一些優(yōu)點(diǎn),如減少內(nèi)存開銷、統(tǒng)一管理全局資源、提供全局訪問點(diǎn)等。然而,過度使用單例模式可能導(dǎo)致代碼的可測試性和可擴(kuò)展性下降,因此在設(shè)計(jì)和應(yīng)用中需要謹(jǐn)慎使用。
2.2 餓漢模式和懶漢模式
餓漢模式(Eager Initialization) 和 懶漢模式(Lazy Initialization) 是兩種常見的單例模式實(shí)現(xiàn)方式。
餓漢模式:
- 在餓漢模式中,單例實(shí)例在類加載時(shí)就被創(chuàng)建,并在整個(gè)程序運(yùn)行期間保持不變。因此,它的實(shí)例化是立即發(fā)生的,不需要延遲加載。
- 餓漢模式的實(shí)現(xiàn)簡單直接,可以通過將構(gòu)造函數(shù)設(shè)為私有并創(chuàng)建一個(gè)靜態(tài)的、final的實(shí)例來實(shí)現(xiàn)。通過靜態(tài)方法或直接訪問實(shí)例變量,其他代碼可以獲得該實(shí)例。
- 餓漢模式是線程安全的,因?yàn)閷?shí)例在類加載時(shí)就被創(chuàng)建,不存在多線程環(huán)境下的競爭條件。
- 由于實(shí)例在類加載時(shí)就創(chuàng)建,可能會(huì)導(dǎo)致資源的浪費(fèi),尤其是在實(shí)例很大或者需要耗費(fèi)較多資源的情況下。
懶漢模式:
- 在懶漢模式中,單例實(shí)例在第一次使用時(shí)才被創(chuàng)建,延遲實(shí)例化。因此,它的實(shí)例化是在需要時(shí)發(fā)生的,具有延遲加載的特性。
- 懶漢模式的實(shí)現(xiàn)通常涉及使用雙重檢查鎖機(jī)制或者使用內(nèi)部類來實(shí)現(xiàn)延遲加載和線程安全。
- 懶漢模式可以避免在應(yīng)用程序啟動(dòng)時(shí)就創(chuàng)建實(shí)例,節(jié)省了資源。然而,在多線程環(huán)境中需要考慮線程安全性,特別是在第一次創(chuàng)建實(shí)例時(shí)需要進(jìn)行同步控制,以避免多個(gè)線程同時(shí)創(chuàng)建多個(gè)實(shí)例的問題。
根據(jù)以上的定義可以得出,餓漢模式適用于實(shí)例創(chuàng)建和初始化的成本較低、資源占用較小的情況。懶漢模式適用于實(shí)例創(chuàng)建和初始化的成本較高、資源占用較大的情況,并且希望在需要時(shí)才進(jìn)行實(shí)例化。同時(shí),在多線程環(huán)境下,需要考慮線程安全性和性能等因素進(jìn)行選擇。
三、單例模式的實(shí)現(xiàn)
3.1 餓漢模式的單例模式
惡漢模式的代碼如下:
class Singleton{
// 此處,先把這個(gè)實(shí)例創(chuàng)建出來
private static Singleton instance = new Singleton();
// 如果要使用這個(gè)實(shí)例,統(tǒng)一通過 Singleton.getInstance() 方式獲取
// 餓漢模式只涉及到讀操作,因此是線程安全的
public static Singleton getInstance(){
return instance;
}
// 為了避免 Singleton 類不小心被復(fù)制出多份
// 把構(gòu)造方法設(shè)置為 private,在類外面就無法通過 new 的方式來創(chuàng)建這個(gè) Singleton 實(shí)例了
private Singleton(){}
}
-
在這個(gè)實(shí)現(xiàn)中,單例實(shí)例
instance
在類加載階段就被創(chuàng)建,并且通過靜態(tài)方法getInstance()
返回該實(shí)例。 -
餓漢模式的實(shí)現(xiàn)確保了在整個(gè)程序運(yùn)行期間只有一個(gè)實(shí)例存在,并且在需要時(shí)立即獲得該實(shí)例。由于實(shí)例在類加載時(shí)就被創(chuàng)建,所以在多線程環(huán)境下也是線程安全的。
-
私有的構(gòu)造方法確保了在類外部無法通過
new
關(guān)鍵字創(chuàng)建該類的實(shí)例,從而限制了實(shí)例的數(shù)量。
3.2 懶漢模式的單例模式
單線程版懶漢模式的代碼實(shí)現(xiàn):
class Singleton {
private static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance() {
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
上面的代碼實(shí)現(xiàn)了單線程環(huán)境下的懶漢模式單例模式。
在這個(gè)實(shí)現(xiàn)中,getInstance()
方法首先檢查 instance
是否為 null,如果為 null,則通過私有的構(gòu)造函數(shù)創(chuàng)建一個(gè)新的實(shí)例,并將其賦值給 instance
變量。在后續(xù)調(diào)用 getInstance()
方法時(shí),由于 instance
已經(jīng)被賦值,直接返回該實(shí)例。
這種實(shí)現(xiàn)方式在單線程環(huán)境下是可行的,能夠保證只有一個(gè)實(shí)例被創(chuàng)建并被返回。但是在多線程環(huán)境下,可能會(huì)出現(xiàn)問題,多個(gè)線程同時(shí)通過 instance == null
的判斷條件進(jìn)入,導(dǎo)致多次創(chuàng)建實(shí)例,違反了單例模式的原則。
因此,在多線程環(huán)境下,需要對(duì)該實(shí)現(xiàn)進(jìn)行改進(jìn),以保證線程安全性。常見的方式是引入同步機(jī)制,如雙重檢查鎖或使用 synchronized
關(guān)鍵字,來確保只有一個(gè)線程能夠創(chuàng)建實(shí)例。
多線程版:
class Singleton{
// 存在指令重排序問題。
// 加上 volatile 關(guān)鍵字保持 instance 內(nèi)存可見性,禁止指令重排
private volatile static Singletoninstance = null;
// 懶漢模式存在現(xiàn)線程安全
// 解決方法:加上 synchronized
// 存在的問題:這里的加鎖只是在new對(duì)象之前加鎖才是有必要的,后面就一直是讀操作,沒有加鎖的必要
// 改進(jìn):
// 使用雙重 if 判定, 降低鎖競爭的頻率
// 給 instance 加上了 volatile.
public static Singleton getInstance() {
if (null == instance) { // 判斷是否加鎖
// 當(dāng) instance 為空時(shí)才加鎖
synchronized (Singleton.class) {
if (null == instance) { // 判斷是否創(chuàng)建對(duì)象
instance = new Singleton();
}
}
}
return instance;
}
private Singleton() {
}
}
以上代碼實(shí)現(xiàn)了多線程環(huán)境下的懶漢模式單例模式,并對(duì)其進(jìn)行了改進(jìn)。
在這個(gè)實(shí)現(xiàn)中,使用了 雙重檢查鎖(Double-Checked Locking) 機(jī)制和 volatile
關(guān)鍵字來解決多線程環(huán)境下的線程安全和指令重排序問題。文章來源:http://www.zghlxwxcb.cn/news/detail-571887.html
將 instance
聲明為 volatile
,可以保證線程之間對(duì) instance
的可見性和禁止指令重排序,避免在實(shí)例化過程中的并發(fā)問題。因?yàn)樵谠缙诘?Java 版本中存在雙重檢查鎖失效的問題。為了避免這個(gè)問題,建議使用 Java 5 及以上版本,并將 instance
變量聲明為 volatile
。文章來源地址http://www.zghlxwxcb.cn/news/detail-571887.html
到了這里,關(guān)于【單例模式】餓漢模式和懶漢模式的單例模式的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!