日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) >

Java 设计模式之单利模式

發(fā)布時(shí)間:2025/3/21 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java 设计模式之单利模式 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一、首先介紹一下單例模式:?
??? 單例模式(Singleton),也叫單子模式,是一種常用的軟件設(shè)計(jì)模式。在應(yīng)用這個(gè)模式時(shí),單例對(duì)象的類必須保證只有一個(gè)實(shí)例存在。許多時(shí)候整個(gè)系統(tǒng)只需要擁有一個(gè)的全局對(duì)象,這樣有利于我們協(xié)調(diào)系統(tǒng)整體的行為。比如在某個(gè)服務(wù)器程序中,該服務(wù)器的配置信息存放在一個(gè)文件中,這些配置數(shù)據(jù)由一個(gè)單例對(duì)象統(tǒng)一讀取,然后服務(wù)進(jìn)程中的其他對(duì)象再通過(guò)這個(gè)單例對(duì)象獲取這些配置信息。這種方式簡(jiǎn)化了在復(fù)雜環(huán)境下的配置管理。?

實(shí)現(xiàn)單例模式的思路是:?
??? 一個(gè)類能返回對(duì)象一個(gè)引用(永遠(yuǎn)是同一個(gè))和一個(gè)獲得該實(shí)例的方法(必須是靜態(tài)方法,通常使用getInstance這個(gè)名 稱);當(dāng)我們調(diào)用這個(gè)方法時(shí),如果類持有的引用不為空就返回這個(gè)引用,如果類保持的引用為空就創(chuàng)建該類的實(shí)例并將實(shí)例的引用賦予該類保持的引用;同時(shí)我們 還將該類的構(gòu)造函數(shù)定義為私有方法,這樣其他處的代碼就無(wú)法通過(guò)調(diào)用該類的構(gòu)造函數(shù)來(lái)實(shí)例化該類的對(duì)象,只有通過(guò)該類提供的靜態(tài)方法來(lái)得到該類的唯一實(shí)例。?

需要注意的地方:?
??? 單例模式在多線程的 應(yīng)用場(chǎng)合下必須小心使用。如果當(dāng)唯一實(shí)例尚未創(chuàng)建時(shí),有兩個(gè)線程同時(shí)調(diào)用創(chuàng)建方法,那么它們同時(shí)沒(méi)有檢測(cè)到唯一實(shí)例的存在,從而同時(shí)各自創(chuàng)建了一個(gè)實(shí)例, 這樣就有兩個(gè)實(shí)例被構(gòu)造出來(lái),從而違反了單例模式中實(shí)例唯一的原則。 解決這個(gè)問(wèn)題的辦法是為指示類是否已經(jīng)實(shí)例化的變量提供一個(gè)互斥鎖(雖然這樣會(huì)降低效率)。

因程序需要,有時(shí)我們只需要某個(gè)類同時(shí)保留一個(gè)對(duì)象,不希望有更多對(duì)象,此時(shí),我們則應(yīng)考慮單例模式的設(shè)計(jì)。

二. 單例模式的特點(diǎn)

1. 單例模式只能有一個(gè)實(shí)例。

2. 單例類必須創(chuàng)建自己的唯一實(shí)例。

3. 單例類必須向其他對(duì)象提供這一實(shí)例。

三. 單例模式VS靜態(tài)類

在知道了什么是單例模式后,我想你一定會(huì)想到靜態(tài)類,“既然只使用一個(gè)對(duì)象,為何不干脆使用靜態(tài)類?”,這里我會(huì)將單例模式和靜態(tài)類進(jìn)行一個(gè)比較。

1. 單例可以繼承和被繼承,方法可以被override,而靜態(tài)方法不可以。

2. 靜態(tài)方法中產(chǎn)生的對(duì)象會(huì)在執(zhí)行后被釋放,進(jìn)而被GC清理,不會(huì)一直存在于內(nèi)存中。

3. 靜態(tài)類會(huì)在第一次運(yùn)行時(shí)初始化,單例模式可以有其他的選擇,即可以延遲加載。

4. 基于2, 3條,由于單例對(duì)象往往存在于DAO層(例如sessionFactory),如果反復(fù)的初始化和釋放,則會(huì)占用很多資源,而使用單例模式將其常駐于內(nèi)存可以更加節(jié)約資源。

5. 靜態(tài)方法有更高的訪問(wèn)效率。

6. 單例模式很容易被測(cè)試。

幾個(gè)關(guān)于靜態(tài)類的誤解:

誤解一:靜態(tài)方法常駐內(nèi)存而實(shí)例方法不是。

實(shí)際上,特殊編寫(xiě)的實(shí)例方法可以常駐內(nèi)存,而靜態(tài)方法需要不斷初始化和釋放。

誤解二:靜態(tài)方法在堆(heap)上,實(shí)例方法在棧(stack)上。

實(shí)際上,都是加載到特殊的不可寫(xiě)的代碼內(nèi)存區(qū)域中。

靜態(tài)類和單例模式情景的選擇:

情景一:不需要維持任何狀態(tài),僅僅用于全局訪問(wèn),此時(shí)更適合使用靜態(tài)類。

情景二:需要維持一些特定的狀態(tài),此時(shí)更適合使用單例模式。

四. 單例模式的優(yōu)缺點(diǎn)

優(yōu)點(diǎn):?
??? 1.在單例模式中,活動(dòng)的單例只有一個(gè)實(shí)例,對(duì)單例類的所有實(shí)例化得到的都是相同的一個(gè)實(shí)例。這樣就 防止其它對(duì)象對(duì)自己的實(shí)例化,確保所有的對(duì)象都訪問(wèn)一個(gè)實(shí)例?
??? 2.單例模式具有一定的伸縮性,類自己來(lái)控制實(shí)例化進(jìn)程,類就在改變實(shí)例化進(jìn)程上有相應(yīng)的伸縮性。?
??? 3.提供了對(duì)唯一實(shí)例的受控訪問(wèn)。?
??? 4.由于在系統(tǒng)內(nèi)存中只存在一個(gè)對(duì)象,因此可以 節(jié)約系統(tǒng)資源,當(dāng) 需要頻繁創(chuàng)建和銷毀的對(duì)象時(shí)單例模式無(wú)疑可以提高系統(tǒng)的性能。?
??? 5.允許可變數(shù)目的實(shí)例。?
??? 6.避免對(duì)共享資源的多重占用。?
缺點(diǎn):?
??? 1.不適用于變化的對(duì)象,如果同一類型的對(duì)象總是要在不同的用例場(chǎng)景發(fā)生變化,單例就會(huì)引起數(shù)據(jù)的錯(cuò)誤,不能保存彼此的狀態(tài)。?
??? 2.由于單利模式中沒(méi)有抽象層,因此單例類的擴(kuò)展有很大的困難。?
??? 3.單例類的職責(zé)過(guò)重,在一定程度上違背了“單一職責(zé)原則”。?
??? 4.濫用單例將帶來(lái)一些負(fù)面問(wèn)題,如為了節(jié)省資源將數(shù)據(jù)庫(kù)連接池對(duì)象設(shè)計(jì)為的單例類,可能會(huì)導(dǎo)致共享連接池對(duì)象的程序過(guò)多而出現(xiàn)連接池溢出;如果實(shí)例化的對(duì)象長(zhǎng)時(shí)間不被利用,系統(tǒng)會(huì)認(rèn)為是垃圾而被回收,這將導(dǎo)致對(duì)象狀態(tài)的丟失。?
使用注意事項(xiàng):?
??? 1.使用時(shí)不能用反射模式創(chuàng)建單例,否則會(huì)實(shí)例化一個(gè)新的對(duì)象?
??? 2.使用懶單例模式時(shí)注意線程安全問(wèn)題?
??? 3.餓單例模式和懶單例模式構(gòu)造方法都是私有的,因而是不能被繼承的,有些單例模式可以被繼承(如登記式模式)?
適用場(chǎng)景:?
??? 單例模式只允許創(chuàng)建一個(gè)對(duì)象,因此節(jié)省內(nèi)存,加快對(duì)象訪問(wèn)速度,因此對(duì)象需要被公用的場(chǎng)合適合使用,如多個(gè)模塊使用同一個(gè)數(shù)據(jù)源連接對(duì)象等等。如:?
??? 1.需要頻繁實(shí)例化然后銷毀的對(duì)象。?
??? 2.創(chuàng)建對(duì)象時(shí)耗時(shí)過(guò)多或者耗資源過(guò)多,但又經(jīng)常用到的對(duì)象。?
??? 3.有狀態(tài)的工具類對(duì)象。?
??? 4.頻繁訪問(wèn)數(shù)據(jù)庫(kù)或文件的對(duì)象。?
以下都是單例模式的經(jīng)典使用場(chǎng)景:?
??? 1.資源共享的情況下,避免由于資源操作時(shí)導(dǎo)致的性能或損耗等。如上述中的日志文件,應(yīng)用配置。?
??? 2.控制資源的情況下,方便資源之間的互相通信。如線程池等。?
應(yīng)用場(chǎng)景舉例:?
??? 1.外部資源:每臺(tái)計(jì)算機(jī)有若干個(gè)打印機(jī),但只能有一個(gè)PrinterSpooler,以避免兩個(gè)打印作業(yè)同時(shí)輸出到打印機(jī)。內(nèi)部資源:大多數(shù)軟件都有一個(gè)(或多個(gè))屬性文件存放系統(tǒng)配置,這樣的系統(tǒng)應(yīng)該有一個(gè)對(duì)象管理這些屬性文件?
??? 2. Windows的Task Manager(任務(wù)管理器)就是很典型的單例模式(這個(gè)很熟悉吧),想想看,是不是呢,你能打開(kāi)兩個(gè)windows task manager嗎? 不信你自己試試看哦~?
??? 3. windows的Recycle Bin(回收站)也是典型的單例應(yīng)用。在整個(gè)系統(tǒng)運(yùn)行過(guò)程中,回收站一直維護(hù)著僅有的一個(gè)實(shí)例。?
??? 4. 網(wǎng)站的計(jì)數(shù)器,一般也是采用單例模式實(shí)現(xiàn),否則難以同步。?
??? 5. 應(yīng)用程序的日志應(yīng)用,一般都何用單例模式實(shí)現(xiàn),這一般是由于共享的日志文件一直處于打開(kāi)狀態(tài),因?yàn)橹荒苡幸粋€(gè)實(shí)例去操作,否則內(nèi)容不好追加。?
??? 6. Web應(yīng)用的配置對(duì)象的讀取,一般也應(yīng)用單例模式,這個(gè)是由于配置文件是共享的資源。?
??? 7. 數(shù)據(jù)庫(kù)連接池的設(shè)計(jì)一般也是采用單例模式,因?yàn)閿?shù)據(jù)庫(kù)連接是一種數(shù)據(jù)庫(kù)資源。數(shù)據(jù)庫(kù)軟件系統(tǒng)中使用數(shù)據(jù)庫(kù)連接池,主要是節(jié)省打開(kāi)或者關(guān)閉數(shù)據(jù)庫(kù)連接所引起的效率損耗,這種效率上的損耗還是非常昂貴的,因?yàn)楹斡脝卫J絹?lái)維護(hù),就可以大大降低這種損耗。?
??? 8. 多線程的線程池的設(shè)計(jì)一般也是采用單例模式,這是由于線程池要方便對(duì)池中的線程進(jìn)行控制。?
??? 9. 操作系統(tǒng)的文件系統(tǒng),也是大的單例模式實(shí)現(xiàn)的具體例子,一個(gè)操作系統(tǒng)只能有一個(gè)文件系統(tǒng)。?
??? 10. HttpApplication 也是單位例的典型應(yīng)用。熟悉ASP.Net(IIS)的整個(gè)請(qǐng)求生命周期的人應(yīng)該知道HttpApplication也是單例模式,所有的HttpModule都共享一個(gè)HttpApplication實(shí)例.?
  ?
實(shí)現(xiàn)單利模式的原則和過(guò)程:?
??? 1.單例模式:確保一個(gè)類只有一個(gè)實(shí)例,自行實(shí)例化并向系統(tǒng)提供這個(gè)實(shí)例?
??? 2.單例模式分類:餓單例模式(類加載時(shí)實(shí)例化一個(gè)對(duì)象給自己的引用),懶單例模式(調(diào)用取得實(shí)例的方法如getInstance時(shí)才會(huì)實(shí)例化對(duì)象)(java中餓單例模式性能優(yōu)于懶單例模式,c++中一般使用懶單例模式)?
??? 3.單例模式要素:?
??????? a.私有構(gòu)造方法?
??????? b.私有靜態(tài)引用指向自己實(shí)例?
??????? c.以自己實(shí)例為返回值的公有靜態(tài)方法

?代碼的實(shí)現(xiàn):

1. 懶漢模式

/**
* 類同步
*/
public
class SingletonDemo {private static SingletonDemo instance;private SingletonDemo(){}public static SingletonDemo getInstance(){if(instance==null){instance=new SingletonDemo();}return instance;} }

/**
* 方法同步
*/
@Component
public class LazySingleton {

private static LazySingleton lazySingleton = null;

public static LazySingleton getInstance(){
if (lazySingleton == null){
synchronized (LazySingleton.class){ // 獲得對(duì)象鎖,然后鎖住這一段代碼
/**
* 在synchronized中的if判斷是為了防止多個(gè)線程湊巧同時(shí)第一次調(diào)用getInstance,
* 它們都會(huì)去進(jìn)入synchronized塊中,因?yàn)閚ew一個(gè)這玩意需要很多資源,所以讓它們?cè)倥抨?duì)后再判斷一次。
*/
if (lazySingleton == null){
lazySingleton = new LazySingleton();
}
}
}
return lazySingleton;
}

private LazySingleton(){
System.out.println("================================ 懶漢單利模式");
}

}
?

如上,通過(guò)提供一個(gè)靜態(tài)的對(duì)象instance,利用private權(quán)限的構(gòu)造方法和getInstance()方法來(lái)給予訪問(wèn)者一個(gè)單例。

缺點(diǎn)是,沒(méi)有考慮到線程安全,可能存在多個(gè)訪問(wèn)者同時(shí)訪問(wèn),并同時(shí)構(gòu)造了多個(gè)對(duì)象的問(wèn)題。之所以叫做懶漢模式,主要是因?yàn)榇朔N方法可以非常明顯的lazy loading。

針對(duì)懶漢模式線程不安全的問(wèn)題,我們自然想到了,在getInstance()方法前加鎖,于是就有了第二種實(shí)現(xiàn)。

2.線程安全的懶漢模式

public class SingletonDemo {private static SingletonDemo instance;private SingletonDemo(){}public static synchronized SingletonDemo getInstance(){if(instance==null){instance=new SingletonDemo();}return instance;} }

然而并發(fā)其實(shí)是一種特殊情況,大多時(shí)候這個(gè)鎖占用的額外資源都浪費(fèi)了,這種打補(bǔ)丁方式寫(xiě)出來(lái)的結(jié)構(gòu)效率很低。

3. 餓漢模式

/*** 餓漢式單例模式* Created by Ulegal on 2017/11/29.*/ @Component public class Singleton {/*** 設(shè)立靜態(tài)變量,直接創(chuàng)建實(shí)例*/private static final Singleton ourInstance = new Singleton();/*** 開(kāi)放一個(gè)公有方法,判斷是否已經(jīng)存在實(shí)例,有返回,沒(méi)有新建一個(gè)在返回* @return*/public static Singleton getInstance() {System.out.println("-->餓漢式單例模式開(kāi)始調(diào)用公有方法返回實(shí)例");return ourInstance;}/*** 私有化構(gòu)造函數(shù)*/private Singleton() {//私有化構(gòu)造函數(shù)System.out.println("-->餓漢式單例模式開(kāi)始調(diào)用構(gòu)造函數(shù)");}

直接在運(yùn)行這個(gè)類的時(shí)候進(jìn)行一次loading,之后直接訪問(wèn)。顯然,這種方法沒(méi)有起到lazy loading的效果,考慮到前面提到的和靜態(tài)類的對(duì)比,這種方法只比靜態(tài)類多了一個(gè)內(nèi)存常駐而已。

4.靜態(tài)類內(nèi)部加載

public class SingletonDemo {private static class SingletonHolder{private static SingletonDemo instance=new SingletonDemo();}private SingletonDemo(){System.out.println("Singleton has loaded");}public static SingletonDemo getInstance(){return SingletonHolder.instance;} }

使用內(nèi)部類的好處是,靜態(tài)內(nèi)部類不會(huì)在單例加載時(shí)就加載,而是在調(diào)用getInstance()方法時(shí)才進(jìn)行加載,達(dá)到了類似懶漢模式的效果,而這種方法又是線程安全的。

?

5.枚舉方法

enum SingletonDemo{INSTANCE;public void otherMethods(){System.out.println("Something");} }

Effective Java作者Josh Bloch 提倡的方式,在我看來(lái)簡(jiǎn)直是來(lái)自神的寫(xiě)法。解決了以下三個(gè)問(wèn)題:

(1)自由序列化。

(2)保證只有一個(gè)實(shí)例。

(3)線程安全。

如果我們想調(diào)用它的方法時(shí),僅需要以下操作:

public class Hello {public static void main(String[] args){SingletonDemo.INSTANCE.otherMethods();} }

這種充滿美感的代碼真的已經(jīng)終結(jié)了其他一切實(shí)現(xiàn)方法了。

6. 雙重校驗(yàn)鎖法

public class SingletonDemo {private volatile static SingletonDemo instance;private SingletonDemo(){System.out.println("Singleton has loaded");}public static SingletonDemo getInstance(){if(instance==null){synchronized (SingletonDemo.class){if(instance==null){instance=new SingletonDemo();}}}return instance;} }

接下來(lái)我解釋一下在并發(fā)時(shí),雙重校驗(yàn)鎖法會(huì)有怎樣的情景:

STEP?1. 線程A訪問(wèn)getInstance()方法,因?yàn)閱卫€沒(méi)有實(shí)例化,所以進(jìn)入了鎖定塊。

STEP 2. 線程B訪問(wèn)getInstance()方法,因?yàn)閱卫€沒(méi)有實(shí)例化,得以訪問(wèn)接下來(lái)代碼塊,而接下來(lái)代碼塊已經(jīng)被線程1鎖定。

STEP 3. 線程A進(jìn)入下一判斷,因?yàn)閱卫€沒(méi)有實(shí)例化,所以進(jìn)行單例實(shí)例化,成功實(shí)例化后退出代碼塊,解除鎖定。

STEP 4. 線程B進(jìn)入接下來(lái)代碼塊,鎖定線程,進(jìn)入下一判斷,因?yàn)橐呀?jīng)實(shí)例化,退出代碼塊,解除鎖定。

STEP 5. 線程A獲取到了單例實(shí)例并返回,線程B沒(méi)有獲取到單例并返回Null。

理論上雙重校驗(yàn)鎖法是線程安全的,并且,這種方法實(shí)現(xiàn)了lazyloading。

?

餓漢模式源碼 : https://gitee.com/xdymemory00/DanLiMoShi.git

?

轉(zhuǎn)載于:https://www.cnblogs.com/memoryXudy/p/7929845.html

總結(jié)

以上是生活随笔為你收集整理的Java 设计模式之单利模式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。