個人博客CoorChice,https://chenbingx.github.io/ ,最新文章將會首發(fā)CoorChice的博客,歡迎探索哦 !
同時,搜索微信公眾號CoorChice
,或掃描文章末尾二維碼,可以關(guān)注我的微信公眾號。同期文章也將會優(yōu)先推送到微信公眾號中,以提醒您有新鮮文章出爐。
封面-設(shè)計模式.png
單例模式是我們最常使用,也是最簡單的一種模式,主要用于只想系統(tǒng)中存在一個實例的情況,比如某個Manager。
定義及實質(zhì)
定義
確保某一個類只有一個實例,而且自行實例化并向系統(tǒng)提供這個實例。實質(zhì)
控制實例數(shù)量,確保只有一個實例。
模式圖解
單例模式UML圖
單例模式UML圖
很直觀明了,很簡單。下面來看看單例模式的不同實現(xiàn)方案。
餓漢式
public class Singleton{
private static fianl Singleton instance = new Singleton();
//私有化構(gòu)造器,避免外部訪問。使用反射仍然可以訪問,所以安全是相對的。
//但仍然可以通過哈希值等進(jìn)行限制,提高安全性。
priavte Singleton(){
}
//對外暴露的接口,用于獲取實例
public static Singleton getInstance(){
return instance;
}
public void doSomething(){
System.out.println("doSomething");
}
}
解釋:
- 餓漢式是利用了static 關(guān)鍵字在類加載時就會進(jìn)行初始化,并且緩存到靜態(tài)內(nèi)存 中的特點,確保了調(diào)用getInstance() 時,無須擔(dān)心instance 為null;
- 通過fianl 關(guān)鍵字,式單例在多線程情況下的安全,因為JVM會自動對fianl 進(jìn)行上鎖同步。
優(yōu)點: 能夠在線程安全的情況下實現(xiàn)單例。
缺點: 由于類一加載就會創(chuàng)建實例,所以會較早占用系統(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)了延遲加載的的思想。對象實例只有在第一次調(diào)用getInstance() 方法時才會被創(chuàng)建,一定程度上的節(jié)約了系統(tǒng)資源;
- 懶漢式在單線程下能夠很好的工作,但是并發(fā)下就很有可能會創(chuàng)建多個實例。
優(yōu)點: 能夠?qū)崿F(xiàn)延遲加載,節(jié)約內(nèi)存。在單線程中能很好工作。
缺點: 并發(fā)下可能會創(chuàng)建多個實例,每次判斷都會耗費一些時間。
DCL雙重檢查實現(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)點: 既能很大程度上確保線程安全,又能實現(xiàn)延遲加載。
缺點: 使用volatile 關(guān)鍵字會使JVM對該段代碼的優(yōu)化喪失,影響性能。并且在一些高并發(fā)的情況下,仍然可能創(chuàng)建多個實例,這稱為雙重檢查鎖定失效 ,有一些書中作者均認(rèn)為這是一種“丑陋”的單例實現(xiàn)方案。
靜態(tài)內(nèi)部類實現(xiàn)單例
public class Singleton{
priavte Singleton(){
}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
//靜態(tài)內(nèi)部類確保了在首次調(diào)用getInstance()的時候才會初始化SingletonHolder,從而導(dǎo)致實例被創(chuàng)建。
//并且由JVM保證了線程的安全。
priavte static class SingletonHolder{
priavte static final Singleton instance = new Singleton();
}
public void doSomething(){
System.out.println("doSomething");
}
}
這是單例模式最好的實現(xiàn)方法之一。
枚舉類實現(xiàn)單例
枚舉能夠確保實例的唯一性,能夠最大程度上確保線程安全,并且提供無償序列化機制。所以在不對延遲加載有太高要求的情況下,使用枚舉創(chuàng)建單例是最佳的方案!
public enum Singleton{
INSTANCE;
public void doSomething(){
System.out.println("doSomething");
}
}
拓展
以下幾種情況下JVM會自動幫助我們完成同步:
- 靜態(tài)初始化器(static{}代碼塊)初始化數(shù)據(jù)時;
- 訪問final字段時;
- 在創(chuàng)建線程之前創(chuàng)建對象;
- 線程可以看見它將要創(chuàng)建的對象時。
CoorChice的公眾號