java面試 1 單例模式的幾種寫法

2021-08-11 04:14:20 字數 3281 閱讀 9140

面試中單例模式大體可分為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 在多執行緒的場景...