一、使用場(chǎng)景
單例設(shè)計(jì)模式是應(yīng)用最廣的設(shè)計(jì)模式,例如:當(dāng)創(chuàng)建一個(gè)對(duì)象需要消耗過(guò)多的資源,如要訪問(wèn)IO或數(shù)據(jù)庫(kù)等資源,這時(shí)就要考慮用單例模式;在一個(gè)應(yīng)用中,應(yīng)該只有一個(gè)ImageLoader實(shí)例,這個(gè)ImageLoader中又有線程池、緩存系統(tǒng)、網(wǎng)絡(luò)請(qǐng)求等,很消耗資源,一般用單例模式。
二、如何實(shí)現(xiàn)
1、構(gòu)造函數(shù)私有化。
2、通過(guò)靜態(tài)方法或者枚舉返回單例對(duì)象。
3、確保單例對(duì)象有且只有一個(gè),尤其是在多線程環(huán)境下。
4、確保單例對(duì)象在反序列化下不會(huì)重新構(gòu)建對(duì)象。
三、代碼實(shí)現(xiàn)
1、餓漢單例模式
public class Singleton {
? ? private static final Singleton instance=new Singleton();
? ? private Singleton() {
? ?}
? ? public static Singleton getInstance() {
? ? ? ? ? ? ? ? ? return instance;
? ? ?}
}
2、懶漢式
懶漢單例模式的優(yōu)點(diǎn)是單例只有在使用時(shí)才會(huì)初始化,在一定程度上節(jié)約了資源。缺點(diǎn)是第一次加載時(shí)需要及時(shí)進(jìn)行實(shí)例化,反應(yīng)稍慢,最大的問(wèn)題是每次調(diào)用getInstance都進(jìn)行同步,造成不必要的同步開(kāi)銷,不建議使用。
public class Singleton {
? ? ? private static Singleton instance;
? ? ? private Singleton(){
? ? }
? ? ? public static Synchronized Singleton getInstance(){
? ? ? ? ? ?if(instance==null){
? ? ? ? ? ? ? ? ? ?instance=new Singleton();
? ? ? ? ? }
? ? ? ? ?return instance;
? ? ?}
}
3、Double Check Load(DCL)
優(yōu)點(diǎn)是既能在需要時(shí)才初始化單例,又能保證線程安全,且單例模式初始化時(shí)調(diào)用getInstance不進(jìn)行同步鎖。缺點(diǎn)是在第一次加載時(shí)反應(yīng)稍慢,由于java 內(nèi)存模型的原因偶爾會(huì)加載失敗,在高并發(fā)環(huán)境下也存在一定缺陷,但是概率較小。除非代碼并發(fā)場(chǎng)景較復(fù)雜,或者在JDK 6版本以下,否則該種模式一般滿足條件。該種模式也是使用較多的模式。
public class Singleton {
? ? ? private static Singleton instance=null;
? ? ? private Singleton(){
? ?}
? ? ?public static Singleton getInstance(){
? ? ? ? ?if(instance==null){
? ? ? ? ? ? ? synchronized(Singleton.class){
? ? ? ? ? ? ? ? ? ? if(instance==null){
? ? ? ? ? ? ? ? ? ? ? ? ? ?instance=new Singleton();
? ? ? ? ? ? ? ? ? ?}
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? ? return instance;
? ? }
}
4、靜態(tài)內(nèi)部類單例模式
此種方法解決DCL模式不能在線程并發(fā)復(fù)雜情況下使用問(wèn)題,此為推薦使用模式。
public class Singleton {
? ? ?private Singleton() {
? ? }
? ? ?public static Singleton getInstance() {
? ? ? ? ? ? ?return SingletonHolder.singleton;
? ?}
/**
*靜態(tài)內(nèi)部類
*/
? ? ? private static class SingletonHolder {
? ? ? ? ? ?private static fina lSingleton singleton=new Singleton();
? ? }
}
5、枚舉單例
此種方法寫(xiě)法簡(jiǎn)單,線程安全,并且防止反序列化重新生成對(duì)象。
publc enum SingletonEnum{
INSTANCE;
}
以上構(gòu)建單例模式方法中,為了避免反序列化重新生成對(duì)象,加入以下方法
private Object readResolve() throws ObjectSteamException{
return instance;
}
6、使用容器實(shí)現(xiàn)單例
這種方式使得我們可以管理多種類型的單例,并且使用時(shí)可以用統(tǒng)一的接口進(jìn)行獲取操作,降低用戶使用成本,也對(duì)用戶隱藏了具體實(shí)現(xiàn),降低了耦合度。
public class SingletonManager {
? ? ?private static Map?objMap = new HashMap();
? ? ?private SingletonManager(){}
? ? ?public static void registerService(String key,Object instance){
? ? ? ? ? ? ? if(!objMap.containsKey(key)){
? ? ? ? ? ? ? ? ? ? ? ?objMap.put(key,instance);
? ? ? ? ?}
}
? ? ?public static ObjectgetService(String key){
? ? ? ? ? ? ? ? ? return objMap.get(key);
? ?}
}
不管以哪種方式實(shí)現(xiàn)單例模式,其核心都是構(gòu)造器私有化,并且通過(guò)靜態(tài)方法獲取唯一實(shí)例,在獲取過(guò)程中必須保證線程安全、防止反序列化重新生成實(shí)例等問(wèn)題。當(dāng)然,選擇那種方式,取決于項(xiàng)目本身。