單例設(shè)計模式(Singleton Pattern)是Java開發(fā)人員了解設(shè)計模式的第一種蟋软,也是最容易理解的,在平時的工作使用的很頻繁的設(shè)計模式之一捧存!
概念
單例設(shè)計模式(Singleton Pattern) :確保每一個類只有一個實例亏较,而且自行實例化并向整個系統(tǒng)提供這個實例,這個類稱為單例類挖炬,它提供全局訪問的方法,屬于創(chuàng)建型模式贼急。
-
定義:
- 私有化該類的構(gòu)造函數(shù)
- 通過new在本類中創(chuàng)建一個本類對象
- 定義一個公有的方法茅茂,將在該類中所創(chuàng)建的對象返回
-
確保對象的唯一性:
- 不允許其他程序用new對象
- 在該類中創(chuàng)建對象
- 對外提供一個可以讓其他程序獲取該對象的方法
單例實現(xiàn):在單例類的內(nèi)部實現(xiàn)只生成一個實例,同時它提供一個靜態(tài)的
getInstance()
工廠方法太抓,讓客戶可以訪問它的唯一實例空闲;為了防止在外部對其實例化,將其構(gòu)造函數(shù)設(shè)計為**私有 走敌;在單例類內(nèi)部定義了一個Singleton
類型的靜態(tài) **對象碴倾,作為外部共享的唯一實例。
作用
單例設(shè)計模式主要是為了避免因為創(chuàng)建多個實例造成的資源浪費,且多個實例由于多次調(diào)用容易導(dǎo)致結(jié)果出現(xiàn)錯誤跌榔,而使用單例設(shè)計模式能夠保證整個應(yīng)用中有且只有一個實例异雁。
實現(xiàn)方式
餓漢式
優(yōu)點:在類加載 的時候就完成了實例化,避免了多線程同步問題僧须。
缺點:由于在類加載的時候就實例化了纲刀,所以沒有達(dá)到Lazy Loading
(懶加載)的效果,也就是說我沒有用到這個實例担平,但是它也會加載示绊,會造成內(nèi)存的浪費(但是這個浪費可以忽略的)
publice class Singleton{
//在類加載的時候就創(chuàng)建一個對象
private static Singleton instance = new Singleton();
//初始化構(gòu)造器
private Singleton(){}
//獲取單例的對象
public static Singleton getInstance(){
return instance;
}
}
懶漢式
基礎(chǔ)版
優(yōu)點:
缺點:當(dāng)多線程工作的時候,如果有多個線程同時運行到if(instance == null)
都判斷為空暂论,那么這兩個線程各自都會創(chuàng)建一個實例面褐,這樣就不是單例了。
public class Singleton{
//先定義對象的引用
private static Singleton instance;
//用作初始化的構(gòu)造器取胎,定義為私有展哭,防止外部的類調(diào)用
private static Singleton(){};
public static Singleton getInstance(){
//如果多個線程在此處,則每個線程都會創(chuàng)建一個實例
if(instance == null){
instance =new Singleton();
}
return instance;
}
}
Synchronized版本
優(yōu)點:加上了synchronized
關(guān)鍵字后闻蛀,getInstance()
方法就會鎖上了匪傍。如果有兩個線程同時執(zhí)行到這個方法是,會有其中一個線程獲得同步鎖循榆,進(jìn)而繼續(xù)執(zhí)行析恢,而另外一個線程需要等待,當(dāng)T1線程執(zhí)行完畢之后秧饮,T2才會執(zhí)行映挂。
缺點:加上了同步鎖之后,會強(qiáng)制除T1以外的所有線程等待盗尸,會嚴(yán)重影響程序的執(zhí)行效率
柑船。
雙重檢查版本(Double-Check)
優(yōu)點:在同步鎖的外面在做一次判斷,如果這個線程的實例沒有被創(chuàng)建過泼各,則放入鞍时;如果被創(chuàng)建過,則直接返回它的實例扣蜻。
缺點:使用volatile
關(guān)鍵字會屏蔽Java虛擬機(jī)所做的一些代碼優(yōu)化逆巍,可能導(dǎo)致系統(tǒng)運行效率低。
注意:如果使用雙重檢查鎖來實現(xiàn)懶漢式單例類莽使,需要在靜態(tài)成員變量實例之前增加修飾符volatile
锐极,被volatile
修飾的成員變量可以確保多個線程都能夠正確處理。
public class Singleton{
//添加volatile 關(guān)鍵字
private static volatile Singleton instance;
private static Singleton();
private static Singleton getInstance(){
//第一次判斷:該線程的實例是否被創(chuàng)建
if(instance == null){
//使用同步代碼塊芳肌,由于此方法是靜態(tài)的灵再,所以同步鎖是類名.class
synchronized(Singleton.class){
//第二次判斷:為了防止了可能出現(xiàn)的多個實例的情況
if(instance == null){
intstance = new Singleton();
}
}
}
return instance;
}
}
靜態(tài)內(nèi)部類
優(yōu)點:餓漢式單例類不是實現(xiàn)延時加載肋层,不管將來用不用始終占據(jù)內(nèi)存;懶漢式單例類線程安全控制繁瑣翎迁,而且性能受影響栋猖。我們在單例類中增加一個**靜態(tài)內(nèi)部類 **,在該內(nèi)部類中創(chuàng)建單例對象汪榔,再將該單例對象通過getinstance
方法返回給外部使用蒲拉。
由于靜態(tài)單例對象沒有作為Singleton
的成員變量直接實例化,因此類加載時不會實例化Singleton
揍异,第一次調(diào)用getInstance()
時將加載內(nèi)部類SingletonHolder
全陨,在該內(nèi)部類中定義了一個static
類型的變量instance
,此時會首先初始化這個變量衷掷,由Java虛擬機(jī)來保證其線程安全性,確保該成員變量只能初始化一次柿菩。
缺點:與編程語言本身的特性相關(guān)戚嗅,很多面向?qū)ο笳Z言不支持IoDH
。
public class Singleton{
//靜態(tài)內(nèi)部類
private static class SingletonHolder{
private static final Singleton instance = new Singleton();
}
private Singleton(){};
//內(nèi)部類是在需要被實例化時枢舶,調(diào)用getInstance()方法時懦胞,才會去裝載SingletonHolder類
public static final Singleton getInstance(){
return SingletonHolder.instance;
}
}
枚舉
優(yōu)點:它不僅能避免多線程同步問題,而且還自動支持序列化機(jī)制凉泄,防止反序列化重新創(chuàng)建新的對象躏尉,絕對防止多次序列化。
缺點:不能通過reflection attack
來調(diào)用構(gòu)造方法后众。
public enum SingletonEnum{
//枚舉類型變量
instance;
public SingletonEnum(){
}
public void whateverMethod(){
}
}
Java中的單例模式
Java的Runtime對象
在Java語言內(nèi)部胀糜,java.lang.Runtime
對象就是一個使用單例模式的例子。在每一個Java程序里面蒂誉,都是唯一的一個Runtime對象教藻,應(yīng)用程序可以與其運行環(huán)境發(fā)生相互作用。
Runtime
類提供了一個靜態(tài)工廠方法getRuntime()
:
public static Runtime getRuntime();
通過調(diào)用此方法右锨,可以獲得Runtime
類唯一的一個實例:
Runtime rt = Runtime.getInstance();
單例模式的優(yōu)缺點
優(yōu)點:
- 單例模式提供了對唯一實例的受控訪問括堤。因為單例類封裝了它的唯一實例,所以它可以嚴(yán)格控制客戶怎樣以及如何訪問它绍移。
- 由于在系統(tǒng)內(nèi)存中只存在一個對象悄窃,因此可以節(jié)約系統(tǒng)資源,對于一些需要頻繁創(chuàng)建和銷毀的對象單例模式無疑可以提高系統(tǒng)的性能蹂窖。
- 允許可變數(shù)目的實例轧抗。基于單例模式我們可以進(jìn)行擴(kuò)展恼策,使用與單例控制相似的方法來獲得指定個數(shù)的對象實例鸦致,既節(jié)省系統(tǒng)資源潮剪,又解決了單例對象共享過多有損性能的問題。
缺點:
- 由于單例模式中沒有抽象層分唾,因此單例類的擴(kuò)展有很大的困難抗碰。
- 單例類的職責(zé)過重,在一定程度上違背了“單一職責(zé)原則”绽乔。因為單例類既充當(dāng)了工廠角色弧蝇,同時又充當(dāng)了產(chǎn)品角色,包含一些業(yè)務(wù)方法折砸,將產(chǎn)品的創(chuàng)建和產(chǎn)品的本身的功能融合到一起看疗。
- 現(xiàn)在很多面向?qū)ο笳Z言的運行環(huán)境都提供了自動垃圾回收的技術(shù),因此睦授,如果實例化對象長時間不被利用两芳,系統(tǒng)會認(rèn)為它是垃圾,會自動銷毀并回收資源去枷,下次利用時又將重新實例化怖辆,這將導(dǎo)致共享的單例對象狀態(tài)的丟失。
應(yīng)用場景
在一下情況下可以考慮使用單例模式:
- 系統(tǒng)只需要一個實例對象删顶,如系統(tǒng)要求提供一個唯一的序列號生成器或資源管理器竖螃,或者需要考慮資源消耗太大而只允許創(chuàng)建一個對象逗余。
- 客戶調(diào)用類的單例實例只允許使用一個公共訪問節(jié)點特咆,除了該公共訪問點,不能通過其他路徑訪問該實例录粱。