問(wèn)題來(lái)源
什么是單例?它的運(yùn)用場(chǎng)景是什么?
單例模式是指保證在系統(tǒng)中只存在某類唯一對(duì)象。運(yùn)用場(chǎng)景隨處可見,例如工具類、Spring容器默認(rèn)new對(duì)象等。
單例模式有幾種實(shí)現(xiàn)方式?
餓漢式、懶漢式、雙重檢查鎖式、內(nèi)部類式、枚舉式。
推薦使用方式?
餓漢式、內(nèi)部類式。
餓漢式
餓漢式顧名思義餓,那么當(dāng)應(yīng)用程序一開始類加載,類的對(duì)象立馬實(shí)例化加載至JVM。
1 public class SingletonClass { 2 /** 3 * 優(yōu)點(diǎn):調(diào)用效率高。 4 * 缺點(diǎn):沒有延遲加載。 5 */ 6 private static SingletonClass instance =new SingletonClass(); 7 8 public static SingletonClass getInstance(){ 9 return instance;10 }11 }
為什么調(diào)用效率高?沒有延遲加載?
答:假設(shè)在高并發(fā)的場(chǎng)景下,有10W+并發(fā)調(diào)用,不需要同步處理??梢灾苯釉诙褍?nèi)存直接獲取對(duì)象不需要任何等待。
同樣,它沒有延遲加載,如果它是需要消耗很大內(nèi)存的對(duì)象,最開始就加載入堆內(nèi)存,而用戶暫時(shí)不需要。這樣就會(huì)嚴(yán)重占用堆內(nèi)存,影響運(yùn)行效率。
懶漢式
導(dǎo)引:腦洞大開的程序員們說(shuō):上述問(wèn)題還不簡(jiǎn)單,當(dāng)調(diào)用的時(shí)候在new對(duì)象不就行。于是出現(xiàn)了懶漢式的雛形版本。
public class SingletonClass { private static SingletonClass instance; public static SingletonClass getInstance(){ if(null==instance){ instance=new SingletonClass(); } return instance; } }
懶漢式顧名思義懶,就是延遲加載,當(dāng)被調(diào)用的時(shí)候再實(shí)例化。
問(wèn)題:如果你是初出茅廬的應(yīng)屆生寫成這樣,估計(jì)面試官也不會(huì)追究什么。如果你是有一年工作年限的程序員,估計(jì)面試官就會(huì)聲討你了。假設(shè),并發(fā)數(shù)10W+,它就將被蹂躪的不堪入目。那么我們需要怎么解決呢?加上同步操作就大功告成。
1 public class SingletonClass { 2 3 //調(diào)用效率低、延遲加載 4 private static SingletonClass instance; 5 6 public static synchronized SingletonClass getInstance(){ 7 if(null==instance){ 8 instance=new SingletonClass(); 9 }10 return instance;11 }12 }
問(wèn)題:從效率維度考慮,估計(jì)這樣已經(jīng)完美了吧?但是,從安全緯度考慮,依然隱隱約約存在問(wèn)題。如果是接觸過(guò)反射、反序列化的同學(xué),我們一起來(lái)繼續(xù)探討。
/** * 通過(guò)反射破壞懶漢式單例 * @author aaron */public class Client { public static void main(String[] args) throws Exception { SingletonClass clazzOne=SingletonClass.getInstance(); SingletonClass clazzTwo=SingletonClass.getInstance(); System.out.println("clazzOne-hasCode:"+clazzOne.hashCode()); System.out.println("clazzTwo-hasCode:"+clazzTwo.hashCode()); Class<SingletonClass> clazz=(Class<SingletonClass>)Class.forName("singleton.SingletonClass"); Constructor<SingletonClass> c=clazz.getConstructor(null); c.setAccessible(true); SingletonClass clazzThree=c.newInstance(); SingletonClass clazzFour=c.newInstance(); System.out.println("clazzThree-hasCode:"+clazzThree.hashCode()); System.out.println("clazzFour-hasCode:"+clazzFour.hashCode()); } }
1 public class SingletonClass implements Serializable{ 2 3 private static SingletonClass instance; 4 5 public static synchronized SingletonClass getInstance(){ 6 if(null==instance){ 7 instance=new SingletonClass(); 8 } 9 return instance;10 }11 12 public static void main(String[] args) throws Exception {13 SingletonClass clazzOne=SingletonClass.getInstance();14 SingletonClass clazzTwo=SingletonClass.getInstance(); 15 System.out.println("clazzOne-hasCode:"+clazzOne.hashCode());16 System.out.println("clazzTwo-hasCode:"+clazzTwo.hashCode());17 18 19 FileOutputStream fos=new FileOutputStream(new File("f:/test.txt"));20 ObjectOutputStream bos=new ObjectOutputStream(fos);21 bos.writeObject(clazzOne);22 bos.close();23 fos.close();24 25 FileInputStream fis=new FileInputStream(new File("f:/test.txt"));26 ObjectInputStream bis=new ObjectInputStream(fis);27 SingletonClass clazzThree=(SingletonClass) bis.readObject();28 System.out.println("clazzThree-hasCode:"+clazzThree.hashCode());29 }30 }
問(wèn)題:這么輕易就被破解了?那怎么解決呢?
public class SingletonClass implements Serializable{ private static SingletonClass instance; private SingletonClass(){ //防止被反射 if(null!=instance){ throw new RuntimeException(); } } public static synchronized SingletonClass getInstance(){ if(null==instance){ instance=new SingletonClass(); } return instance; } //當(dāng)沒有定義這方法時(shí),反序列化默認(rèn)是重新new對(duì)象。 //反序列化時(shí),如果定義了readResolve()則直接返回此方法指定的對(duì)象。而不需要單獨(dú)再創(chuàng)建新對(duì)象! private Object readResolve() throws ObjectStreamException{ return instance; } }
雙重檢查鎖與內(nèi)部類
雙重檢查鎖與內(nèi)部類的方式:緣由懶漢式、餓漢式要么存在調(diào)用效率低或者運(yùn)行效率低問(wèn)題。而這兩種方式取前兩者的優(yōu)點(diǎn)為自己所用。
1 /** 2 * 單例模式-雙重檢查鎖 3 * @author aaron 4 */ 5 public class SingletonClass{ 6 private static SingletonClass instance; 7 8 public static SingletonClass getInstance(){ 9 if(null==instance){10 synchronized (SingletonClass.class) {11 if(instance==null){12 instance=new SingletonClass();13 }14 }15 }16 return instance;17 }18 }
問(wèn)題:緣由JVM對(duì)于此種方式的同步控制,并不穩(wěn)定,當(dāng)高并發(fā)的時(shí)候,可能會(huì)出現(xiàn)問(wèn)題,并不推薦使用這種方式。理論上來(lái)說(shuō),它是不存在問(wèn)題的。
1 /** 2 * 單例模式-內(nèi)部類的方式 3 * @author aaron 4 */ 5 public class SingletonClass{ 6 7 private static class InnerClass{ 8 public static SingletonClass instance=new SingletonClass(); 9 }10 11 public static SingletonClass getInstance(){12 return InnerClass.instance;13 }14 }
1 /**2 * 單例模式-枚舉的方式3 * @author aaron4 */5 public enum SingletonClass{6 INSTANCE7 }
版權(quán)聲明
作者:xiaoyongAaron(邱勇)
出處:http://www.cnblogs.com/qiuyong/
您的支持是對(duì)博主深入思考總結(jié)的最大鼓勵(lì)。
本文版權(quán)歸作者所有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁(yè)面明顯位置給出原文連接,尊重作者的勞動(dòng)成果。
一直特立獨(dú)行的二本僧,書寫屬于他的天空
http://www.cnblogs.com/qiuyong/p/6917801.html