個(gè)人博客CoorChice草冈,https://chenbingx.github.io/ 牺勾,最新文章將會(huì)首發(fā)CoorChice的博客猜嘱,歡迎探索哦 !
同時(shí)琼娘,搜索微信公眾號(hào)CoorChice
,或掃描文章末尾二維碼攀芯,可以關(guān)注我的微信公眾號(hào)屯断。同期文章也將會(huì)優(yōu)先推送到微信公眾號(hào)中,以提醒您有新鮮文章出爐侣诺。
單例模式是我們最常使用殖演,也是最簡單的一種模式,主要用于只想系統(tǒng)中存在一個(gè)實(shí)例的情況年鸳,比如某個(gè)Manager趴久。
定義及實(shí)質(zhì)
定義
確保某一個(gè)類只有一個(gè)實(shí)例,而且自行實(shí)例化并向系統(tǒng)提供這個(gè)實(shí)例搔确。實(shí)質(zhì)
控制實(shí)例數(shù)量彼棍,確保只有一個(gè)實(shí)例。
模式圖解
單例模式UML圖
很直觀明了妥箕,很簡單滥酥。下面來看看單例模式的不同實(shí)現(xiàn)方案。
餓漢式
public class Singleton{
private static fianl Singleton instance = new Singleton();
//私有化構(gòu)造器畦幢,避免外部訪問坎吻。使用反射仍然可以訪問,所以安全是相對(duì)的宇葱。
//但仍然可以通過哈希值等進(jìn)行限制瘦真,提高安全性。
priavte Singleton(){
}
//對(duì)外暴露的接口黍瞧,用于獲取實(shí)例
public static Singleton getInstance(){
return instance;
}
public void doSomething(){
System.out.println("doSomething");
}
}
解釋:
- 餓漢式是利用了static 關(guān)鍵字在類加載時(shí)就會(huì)進(jìn)行初始化诸尽,并且緩存到靜態(tài)內(nèi)存 中的特點(diǎn),確保了調(diào)用getInstance() 時(shí)印颤,無須擔(dān)心instance 為null您机;
- 通過fianl 關(guān)鍵字,式單例在多線程情況下的安全年局,因?yàn)镴VM會(huì)自動(dòng)對(duì)fianl 進(jìn)行上鎖同步际看。
優(yōu)點(diǎn): 能夠在線程安全的情況下實(shí)現(xiàn)單例。
缺點(diǎn): 由于類一加載就會(huì)創(chuàng)建實(shí)例矢否,所以會(huì)較早占用系統(tǒng)資源仲闽。
懶漢式
public class Singleton{
private static Singleton instance;
priavte Singleton(){
}
//加synchronized上鎖,可以一定程度上確保安全性
public static synchronized Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
public void doSomething(){
System.out.println("doSomething");
}
}
解釋:
- 懶漢式體現(xiàn)了延遲加載的的思想僵朗。對(duì)象實(shí)例只有在第一次調(diào)用getInstance() 方法時(shí)才會(huì)被創(chuàng)建赖欣,一定程度上的節(jié)約了系統(tǒng)資源屑彻;
- 懶漢式在單線程下能夠很好的工作,但是并發(fā)下就很有可能會(huì)創(chuàng)建多個(gè)實(shí)例顶吮。
優(yōu)點(diǎn): 能夠?qū)崿F(xiàn)延遲加載社牲,節(jié)約內(nèi)存。在單線程中能很好工作悴了。
缺點(diǎn): 并發(fā)下可能會(huì)創(chuàng)建多個(gè)實(shí)例膳沽,每次判斷都會(huì)耗費(fèi)一些時(shí)間。
DCL雙重檢查實(shí)現(xiàn)單例
public class Singleton{
//這里使用了volatile關(guān)鍵字让禀,它能夠確保insatnce變量每次都直接從主內(nèi)存(而不是寄存器)中加載最新賦值。
private volatile static Singleton instance = null;
priavte Singleton(){
}
//這里進(jìn)行了兩次null檢查陨界,即雙重檢查鎖定巡揍,這能很大程度的確保安全性
public static Singleton getInstance(){
if(instance == null){
synchroniazed(Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
public void doSomething(){
System.out.println("doSomething");
}
}
優(yōu)點(diǎn): 既能很大程度上確保線程安全,又能實(shí)現(xiàn)延遲加載菌瘪。
缺點(diǎn): 使用volatile 關(guān)鍵字會(huì)使JVM對(duì)該段代碼的優(yōu)化喪失腮敌,影響性能。并且在一些高并發(fā)的情況下俏扩,仍然可能創(chuàng)建多個(gè)實(shí)例糜工,這稱為雙重檢查鎖定失效 ,有一些書中作者均認(rèn)為這是一種“丑陋”的單例實(shí)現(xiàn)方案录淡。
靜態(tài)內(nèi)部類實(shí)現(xiàn)單例
public class Singleton{
priavte Singleton(){
}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
//靜態(tài)內(nèi)部類確保了在首次調(diào)用getInstance()的時(shí)候才會(huì)初始化SingletonHolder捌木,從而導(dǎo)致實(shí)例被創(chuàng)建。
//并且由JVM保證了線程的安全嫉戚。
priavte static class SingletonHolder{
priavte static final Singleton instance = new Singleton();
}
public void doSomething(){
System.out.println("doSomething");
}
}
這是單例模式最好的實(shí)現(xiàn)方法之一刨裆。
枚舉類實(shí)現(xiàn)單例
枚舉能夠確保實(shí)例的唯一性,能夠最大程度上確保線程安全彬檀,并且提供無償序列化機(jī)制帆啃。所以在不對(duì)延遲加載有太高要求的情況下,使用枚舉創(chuàng)建單例是最佳的方案窍帝!
public enum Singleton{
INSTANCE;
public void doSomething(){
System.out.println("doSomething");
}
}
拓展
以下幾種情況下JVM會(huì)自動(dòng)幫助我們完成同步:
- 靜態(tài)初始化器(static{}代碼塊)初始化數(shù)據(jù)時(shí)努潘;
- 訪問final字段時(shí);
- 在創(chuàng)建線程之前創(chuàng)建對(duì)象坤学;
- 線程可以看見它將要?jiǎng)?chuàng)建的對(duì)象時(shí)疯坤。