轉(zhuǎn)載請(qǐng)注明出處:http://tedyin.me/2016/03/13/singlton-pattern/
今天給大家介紹一下單例模式
,就是這個(gè)出場率特別高的模式,是個(gè)程序員基本都用過他停忿,沒用過至少也都知道他。可是我們真的了解他嗎啼辣?
單例模式通常的實(shí)現(xiàn)方式分為以下兩種:
餓漢式
public class Singleton{
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton newInstance(){
return instance;
}
public void doSomething(){
// Do something ...
}
}
餓漢式
是最簡單的實(shí)現(xiàn)方式协饲,這種實(shí)現(xiàn)方式適合那些在初始化時(shí)就要用到單例的情況,這種方式簡單粗暴凸主,如果不單例初始化非抽偃快,而且占用內(nèi)存非常小的時(shí)候這種方式是比較合適的,直接在應(yīng)用啟動(dòng)時(shí)加載并初始化旁舰。
但是锋华,如果單例初始化的操作耗時(shí)比較長而應(yīng)用對(duì)于啟動(dòng)速度又有要求,或者單例的占用內(nèi)存比較大箭窜,再或者單例只是在某個(gè)特定場景的情況下才會(huì)被使用毯焕,而一般情況下是不會(huì)使用時(shí),使用餓漢式
的單例模式就是不合適的磺樱,這時(shí)候就需要用到懶漢式
的方式去按需延遲加載單例纳猫。
懶漢式
public class Singleton{
private static Singleton instance = null;
private Singleton(){}
public static newInstance(){
if(null == instance){
instance = new Singleton();
}
return instance;
}
public void doSomething(){
// Do something ...
}
}
懶漢式
與餓漢式
的最大區(qū)別就是將單例的初始化操作,延遲到需要的時(shí)候才進(jìn)行竹捉,這樣做在某些場合中有很大用處芜辕。比如某個(gè)單例用的次數(shù)不是很多,但是這個(gè)單例提供的功能又非常復(fù)雜块差,而且加載和初始化要消耗大量的資源物遇,這個(gè)時(shí)候使用懶漢式
就是非常不錯(cuò)的選擇。
多線程下的單例模式
上面介紹了一些單例模式的基本應(yīng)用方法憾儒,但是上面所說的那些使用方式都是有一個(gè)隱含的前提询兴,那就是他們都是應(yīng)用在單線程條件下,一旦換成了多線程就有出錯(cuò)的風(fēng)險(xiǎn)起趾。
如果在多線程的情況下诗舰,餓漢式
不會(huì)出現(xiàn)問題,因?yàn)镴VM只會(huì)加載一次單利類训裆,但是懶漢式
可能就會(huì)出現(xiàn)重復(fù)創(chuàng)建單利對(duì)象的問題眶根。為什么會(huì)有這樣的問題呢?因?yàn)?code>懶漢式在創(chuàng)建單例時(shí)是 線程不安全的边琉,多個(gè)線程可能會(huì)并發(fā)調(diào)用他的newInstance
方法導(dǎo)致多個(gè)線程可能會(huì)創(chuàng)建多份相同的單例出來属百。
那有沒有辦法,使餓漢式
的單利模式也是線程安全的呢变姨?答案肯定是有的族扰,大家通常會(huì)使用加同步鎖的方式去實(shí)現(xiàn),但是這樣實(shí)現(xiàn)起來比較麻煩定欧。那么有沒有更好的實(shí)現(xiàn)方式呢渔呵?能問這個(gè)問題那必須是有的,否則就得打臉了砍鸠。 我們可以利用JVM的類加載機(jī)制去實(shí)現(xiàn)扩氢。在很多情況下JVM已經(jīng)為我們提供了同步控制,比如:
- 在
static{}
區(qū)塊中初始化的數(shù)據(jù) - 訪問
final
字段時(shí) - ...
- 等等
因?yàn)樵贘VM進(jìn)行類加載的時(shí)候他會(huì)保證數(shù)據(jù)是同步的爷辱,我們可以這樣實(shí)現(xiàn):
采用內(nèi)部類录豺,在這個(gè)內(nèi)部類里面去創(chuàng)建對(duì)象實(shí)例朦肘。這樣的話,只要應(yīng)用中不使用內(nèi)部類 JVM 就不會(huì)去加載這個(gè)單例類双饥,也就不會(huì)創(chuàng)建單例對(duì)象媒抠,從而實(shí)現(xiàn)
懶漢式
的延遲加載和線程安全。
實(shí)現(xiàn)代碼如下:
public class Singleton{
//內(nèi)部類兢哭,在裝載該內(nèi)部類時(shí)才會(huì)去創(chuàng)建單利對(duì)象
private static class SingletonHolder{
public static Singleton instance = new Singleton();
}
private Singleton(){}
public static Singleton newInstance(){
return SingletonHolder.instance;
}
public void doSomething(){
//do something
}
}
這樣實(shí)現(xiàn)出來的單例類就是線程安全的领舰,麻麻再也不用擔(dān)心我的單例不是單例了。
使用枚舉實(shí)現(xiàn)單例模式
除了上述的單例模式的實(shí)現(xiàn)方式外迟螺,我們還可以通過枚舉類來實(shí)現(xiàn)單利模式冲秽,這也是Effective Java
中推薦的方式。
使用枚舉類型實(shí)現(xiàn)單例模式如下:
public enum Singleton{
//定義一個(gè)枚舉的元素矩父,它就是Singleton的一個(gè)實(shí)例
instance;
public void doSomething(){
// do something ...
}
}
用枚舉實(shí)現(xiàn)是不是更簡單呢锉桑?枚舉方式實(shí)現(xiàn)的單例模式也是線程安全的,所以大家不用擔(dān)心多線程問題窍株,可以大膽去用民轴。
以上就是單例模式的使用,現(xiàn)在完全可以說球订,你真的會(huì)用單例模式了后裸。