單例模式,是設(shè)計模式中最簡單的一種。通過單例模式可以保證系統(tǒng)中的某個類只有一個實例而且該實例易于外界訪問,從而方便對實例個數(shù)的控制并節(jié)約系統(tǒng)資源痰腮。如果希望讓系統(tǒng)中某個類的對象只能存在一個,單例模式就是最好的解決方案律罢。但是膀值,單例模式真的能夠?qū)崿F(xiàn)實例的唯一性嗎?
答案是否定的误辑,很多人都知道使用反射可以破壞單例模式沧踏,除了反射以外,使用序列化與反序列化也同樣會破壞單例巾钉。
比如創(chuàng)建一個User 單例:
這種創(chuàng)建方式就是常說的餓漢模式:在User類被JVM加載的時候就創(chuàng)建好一個唯一的實例翘狱;以后都是用這個實例;同時,由于該實例在類被加載的時候就創(chuàng)建出來了砰苍,所以也避免了線程安全問題潦匈。
還有一種餓漢模式的變種:
這兩種方法都是在jvm加載類的時候就是實例對象;本質(zhì)是一樣的赚导;
由于餓漢模式是在類被加載的時候?qū)ο缶蜁嵗缢酢_@也許會造成不必要的消耗,因為有可能這個實例根本就不會被用到吼旧。而且凰锡,如果這個類被多次加載的話也會造成多次實例化。其實解決這個問題的方式有很多,下面提供兩種解決方式寡夹,第一種是使用靜態(tài)內(nèi)部類的形式处面。第二種是使用懶漢式厂置。
這種方式同樣利用了classloder的機(jī)制來保證初始化User時只有一個線程菩掏,它跟餓漢式不同的是(很細(xì)微的差別):餓漢式是只要User類被裝載了,那么user實例就會被實例化(沒有達(dá)到lazy loading效果)昵济,而這種方式是User類被裝載了智绸,instance不一定被初始化。因為UserSingleton類沒有被主動使用访忿,只有顯示通過調(diào)用Instance方法時瞧栗,才會顯示裝載UserSingleton類,從而實例化user海铆。想象一下迹恐,如果實例化user很消耗資源,我想讓他延遲加載卧斟,另外一方面殴边,我不希望在Singleton類加載時就實例化,因為我不能確保User類還可能在其他的地方被主動使用從而被加載珍语,那么這個時候?qū)嵗痷ser顯然是不合適的锤岸。這個時候,這種方式相比餓漢式更加合理板乙。
懶漢模式:
上面這種單例叫做懶漢式單例是偷。懶漢,就是不會提前把實例創(chuàng)建出來募逞,將類對自己的實例化延遲到第一次被引用的時候蛋铆。Instance方法的作用是希望該對象在第一次被使用的時候被new出來。
這種寫法能夠在多線程中很好的工作放接,而且看起來它也具備很好的延遲加載刺啦,但是,遺憾的是透乾,他效率很低洪燥,因為99%情況下不需要同步。(因為上面的synchronized的加鎖范圍是整個方法乳乌,該方法的所有操作都是同步進(jìn)行的捧韵,但是對于非第一次創(chuàng)建對象的情況,也就是沒有進(jìn)入if語句中的情況汉操,根本不需要同步操作再来,可以直接返回user。)
通過使用同步代碼塊的方式減小了鎖的范圍。這樣可以大大提高效率芒篷。(對于已經(jīng)存在user的情況搜变,無須同步,直接return)针炉。