面試中單例模式大體可分為4類,下面分別介紹他們的基本形式、變種及特點。
/**
* 飽漢模式--執行緒不安全
* 基礎的飽漢-飽漢,即已經吃飽,不著急再吃,餓的時候再吃。所以他就先不初始化單例,等第一次使用的時候再初始化,即「懶載入」。
* 飽漢模式的核心就是懶載入。
* 好處是更啟動速度快、節省資源,一直到例項被第一次訪問,才需要初始化單例;小壞處是寫起來麻煩,大壞處是執行緒不安全,if語句存在競態條件。
* @author zengdq
* @date 2017-11-22
*/public class singleton1
/** 靜態公有方法instance()*/
public static singleton1 getinstance()
return instance;
} }
/**
* 飽漢變種 1--執行緒安全
* 最粗暴的犯法是用synchronized關鍵字修飾getinstance()方法,這樣能達到絕對的執行緒安全。
* 變種1的好處是寫起來簡單,且絕對執行緒安全;壞處是併發效能極差,事實上完全退化到了序列。
* 單例只需要初始化一次,但就算初始化以後,synchronized的鎖也無法避開,從而getinstance()完全變成了序列操作。效能不敏感的場景建議使用。
* @author zengdq
* @date 2017-11-22
*/public class singleton1_1
/** 靜態公有方法instance()*/
public synchronized static singleton1_1 getinstance()
return instance;
} }
/**
* 飽漢變種 2--執行緒不安全
* 變種2是「臭名昭著」的dcl 1.0。
* 針對變種1中單例初始化後鎖仍然無法避開的問題,變種2在變種1的外層又套了一層check,加上synchronized內層的check,即所謂「雙重檢查鎖」(double check lock,簡稱dcl)。
* 變種2的核心是dcl,看起來變種2似乎已經達到了理想的效果:懶載入+執行緒安全。
* 可惜的是,正如注釋中所說,dcl仍然是執行緒不安全的,由於指令重排序,你可能會得到「半個物件」。
* @author zengdq
* @date 2017-11-22
*/public class singleton1_2
/** 靜態公有方法instance()*/
public synchronized static singleton1_2 getinstance()
}} return instance;
} }
/**
* 飽漢變種3--執行緒安全
* 變種3專門針對變種2,可謂dcl 2.0。
* 針對變種3的「半個物件」問題,變種3在instance上增加了volatile關鍵字。
* 多執行緒環境下,變種3更適用於效能敏感的場景。
* @author zengdq
* @date 2017-11-22
*/public class singleton1_3
/** 靜態公有方法instance()*/
public synchronized static singleton1_3 getinstance()
}} return instance;
} }
/**
* 餓漢模式--執行緒安全
* 與飽漢相對,餓漢很餓,只想著盡早吃到。所以他就在最早的時機,即類載入時初始化單例,以後訪問時直接返回即可。
* 餓漢的好處是天生的執行緒安全(得益於類載入機制),寫起來超級簡單,使用時沒有延遲;壞處是有可能造成資源浪費(如果類載入後就一直不使用單例的話)。
* 值得注意的時,單執行緒環境下,餓漢與飽漢在效能上沒什麼差別;但多執行緒環境下,由於飽漢需要加鎖,餓漢的效能反而更優。
* @author zengdq
* @date 2017-11-22
*/public class singleton2
/** 靜態公有方法instance()*/
public static singleton2 getinstance()
}
/**
* holder模式--執行緒安全
* 我們既希望利用餓漢模式中靜態變數的方便和執行緒安全;又希望通過懶載入規避資源浪費。
* holder模式滿足了這兩點要求:核心仍然是靜態變數,足夠方便和執行緒安全;通過靜態的holder類持有真正例項,間接實現了懶載入。
* 相對於餓漢模式,holder模式僅增加了乙個靜態內部類的成本,與飽漢的變種3效果相當(略優),都是比較受歡迎的實現方式。同樣建議考慮。
* @author zengdq
* @date 2017-11-22
*/public class singleton3
};/** 私有建構函式*/
private singleton3()
/** 靜態公有方法instance()*/
public static singleton3 getinstance()
}
/**
* 列舉模式--執行緒安全
* 將列舉的靜態成員變數作為單例的例項
* **量比餓漢模式更少。
* 但使用者只能直接訪問例項singleton4.singleton——事實上,這樣的訪問方式作為單例使用也是恰當的,只是犧牲了靜態工廠方法的優點,如無法實現懶載入
* @author zengdq
* @date 2017-11-22
*/public enum singleton4
上面的分析都忽略了反射和序列化的問題。通過反射或序列化,我們仍然能夠訪問到私有構造器,
建立新的例項破壞單例模式。
此時,只有列舉模式能天然防範這一問題。
下面繼續忽略反射和序列化的問題,做個總結回味一下:
java的幾種單例模式
1 賴漢式 public class lazysingleton public static lazysingleton getinstance 2 餓漢式 public class hungrysingleton public static hungrysingleton getinstance ...
java單例模式的幾種寫法
private static final userservice userservice new userservice private userservice 採取預載入的方式,userservice在 classloader 載入userservice.class 已經宣告了物件 return ...
Java單例模式的幾種坑
在乙個jvm程序中,乙個類對應的例項物件有且只有乙個。因為在乙個程式中,有些業務邏輯和流程是重複的 通用的,沒有必要在每次執行時再進行new相同物件的操作。只進行一次new操作,沒有物件的頻繁建立和 提高了jvm的執行響應速度。尤其是在高併發的情況下,對程式的執行有很大的提公升。1 在多執行緒的場景...