java 設(shè)計模式之單例模式
java 設(shè)計模式之單例模式
前言:
在軟件開發(fā)過程中常會有一些對象我們只需要一個,如:線程池(threadpool)、緩存(cache)、對話框、偏好設(shè)置等。這些對象如果制造出多個實例的話可能會導(dǎo)致一些不必要的麻煩,如:程序行為異常、資源使用過量等。這時單例模式就可以確保一個類只有一個實例,并提供全局訪問點。下面是從簡單的單例類來探討該用何種方法實現(xiàn)單例模式。
/** * 最經(jīng)典的單例類 */ public class Singleton { // 設(shè)置成靜態(tài)變量來記錄Singleton的唯一實例 private static Singleton singleInstance; private Singleton(){ // 構(gòu)造方法聲明為私有的,這樣只能在Singleton類中才能調(diào)用此構(gòu)造方法 } /* * 獲取Singleton對象,如果還未實例化則實例化一個對象并返回這個實例 */ public static Singleton getInstance(){ if (singleInstance == null) { singleInstance = new Singleton(); } return singleInstance; } // 其他方法 }
從上面的例子可以看出Singleton類自己管理這個類的實例化過程,而且提供了全局訪問點,就是設(shè)置成靜態(tài)的getInstance()方法,在其他類要使用Singleton時它會返回一個實例。這中單例模式有個優(yōu)點就是延遲實例化,簡單地說延遲實例化就是延遲初始化,在類需要時才創(chuàng)建其實例,而不是在開始加載這個類時就創(chuàng)建出一個實例,這樣的好處是可以避免性能的浪費。例如有些對象無需程序一開始就使用,或者其在程序執(zhí)行的過程中就沒有使用過。但是此例子卻又一個缺點,那就是線程不夠安全。因為如果有多個線程同時執(zhí)行到getInstance()方法,而Singleton又還未new Singleton()一個實例,那么線程就會都認(rèn)為singleInstance為null,就都會實例化Singleton,這時就會產(chǎn)生多個Singleton實例,明顯不符合單例模式的初衷。那么接下來可能要做的就是對其進(jìn)行改進(jìn)
public class SingletonA { private static SingletonA singletongA; private SingletonA(){ } /* * 增加synchronized關(guān)鍵字把getSingletonA方法變?yōu)橥椒椒? */ public static synchronized SingletonA getInstanceA(){ if (singletongA == null) { singletongA = new SingletonA(); } return singletongA; } // 其他方法 }
從這個例子上看增加了synchronized可以使getInstanceA()變成一個同步的方法,這時線程在進(jìn)入這個方法之前就需要等待其他線程離開這個方法才能進(jìn)入,也就使得該方法只能同時存在一個線程在執(zhí)行它。
可能差不多問題解決了,但是要知道同步方法是會影響程序執(zhí)行效率的,在此例子中我們只是為了解決第一個例子中第一次執(zhí)行g(shù)etInstance()方法不會產(chǎn)生多個實例,而這個例子中卻會導(dǎo)致每次需要實例時都會調(diào)用getInstanceA()同步方法,而在已經(jīng)有實例之后的調(diào)用synchronized就會是累贅,因為我們已經(jīng)無需擔(dān)心這個單例類會再次被創(chuàng)建出新的實例。因此我們還需要做一下改進(jìn)。
既然上面說到延遲實例化,那么如果是不用的話那就簡單多了。
public class SingletonB { // 在靜態(tài)初始化器(static initializen)中創(chuàng)建單例,保證線程安全 private static SingletonB singletonB = new SingletonB(); private SingletonB(){ // 構(gòu)造函數(shù) } public static SingletonB getInstaceB(){ // 已經(jīng)實例化了,直接使用它 return singletonB; } }
上面的這種做法是在JVM加載這個類時馬上創(chuàng)建一個實例,因為JVM會在線程訪問這個實例之前就創(chuàng)建出該實例,因此線程是安全的。但這相較于延遲實例化而言可能會出現(xiàn)資源的浪費。而且如果此類較大的情況下會時程序初始化時間加長。
那么是否可以在使用用延遲實例化的同時又不會造成線程不安全且增加訪問效率呢。接下來就用雙重檢查加鎖來改進(jìn)一下。
/** * 雙重鎖單例模式 */ public class SingletonC { private volatile static SingletonC singletonC; private SingletonC(){ } public static SingletonC getInstanceC(){ if (singletonC == null) { synchronized (SingletonC.class) { if (singletonC == null) { singletonC = new SingletonC(); } } } return singletonC; } }
上面的例子是先檢查實例,如果不存在則進(jìn)入同步區(qū)塊,進(jìn)入同步區(qū)塊之后再次檢查,如果還是null才會創(chuàng)建實例,因而singletonC = new SingletonC()只會執(zhí)行一次,而之后調(diào)用getInstanceC()時就因為有實例直接返回,所以除了第一次調(diào)用時會走同步,而之后便不會如第二個例子那樣每次都會走同步方法。這樣就可以使得執(zhí)行g(shù)etInstanceC()的時間減少。想必這里會發(fā)現(xiàn)有個volatile關(guān)鍵字,其作用是使得singletonC被初始化后對所有線程可見,多個線程可以正確地處理這個SingletonC變量。但要注意的:volatile關(guān)鍵字只能在Java 5及其之后使用,如果在此版本之前會導(dǎo)致這個雙重檢查失效。
在使用單例模式時,如果有多個類加載器(classloader)時需要自行指定類加載器,并指定用一個類加載器。因為每個類加載器都定義了一個命名空間,不同的類加載器可能會加載同一個類,從而導(dǎo)致單例類創(chuàng)建出多個實例。
感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
相關(guān)文章
springboot獲取微信JSDK簽名信息的實現(xiàn)示例
本文介紹了如何在Spring Boot應(yīng)用中獲取微信JSDK的簽名信息,包括獲取接口URL、參數(shù)設(shè)置、簽名算法和獲取簽名結(jié)果的步驟,具有一定的參考價值,感興趣的可以了解一下2023-11-11SpringCloud網(wǎng)關(guān)Gateway架構(gòu)解析
這篇文章主要介紹了SpringCloud網(wǎng)關(guān)Gateway架構(gòu)解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-03-03Java實現(xiàn)的對稱加密算法3DES定義與用法示例
這篇文章主要介紹了Java實現(xiàn)的對稱加密算法3DES定義與用法,結(jié)合實例形式簡單分析了Java 3DES加密算法的相關(guān)定義與使用技巧,需要的朋友可以參考下2018-04-04Lombok同時使?@Data和@Builder踩坑總結(jié)
這篇文章主要介紹了Lombok同時使?@Data和@Builder踩坑總結(jié),文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值需要的小伙伴可以參考一下,希望對你的學(xué)習(xí)有所幫助2022-05-05Spring Cloud Hystrix 服務(wù)容錯保護(hù)的原理實現(xiàn)
這篇文章主要介紹了Spring Cloud Hystrix 服務(wù)容錯保護(hù)的原理實現(xiàn),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-05-05

詳解Java中的迭代迭代器Iterator與枚舉器Enumeration