1.單件(單態(tài),Singleton)模式部分
*有些對(duì)象我們只需要一個(gè),比如說(shuō):線程池(threadpool)、緩存(cache)、對(duì)話框()、處理偏好設(shè)置的對(duì)象、處理注冊(cè)表(register)的對(duì)象、日志對(duì)象,以及充當(dāng)打印機(jī)、顯卡等設(shè)備的驅(qū)動(dòng)程序?qū)ο蟆_@些對(duì)象只能有一個(gè)實(shí)例,如果出現(xiàn)多個(gè)實(shí)例就會(huì)導(dǎo)致程序的行為異常、資源使用過(guò)量,或者產(chǎn)生的結(jié)果不一致等等問(wèn)題。 *單件模式與全局靜態(tài)變量的區(qū)別: (1)使用全局靜態(tài)變量需要程序員之間的約定才能保證只有一個(gè)實(shí)例,而單件模式無(wú)需這樣的約定就可以確保只有一個(gè)實(shí)例被創(chuàng)建。 (2)靜態(tài)變量在程序一開(kāi)始就被創(chuàng)建(這取決于JVM的實(shí)現(xiàn)),而單件模式只是在使用時(shí)才創(chuàng)建對(duì)象。如果這個(gè)被創(chuàng)建的對(duì)象非常消耗資源,而在程序運(yùn)行的過(guò)程中沒(méi)有用到它,就會(huì)造成很大的浪費(fèi),這是靜態(tài)變量的缺點(diǎn)。 *在單態(tài)模式中,如果不需要這個(gè)實(shí)例,它就永遠(yuǎn)不會(huì)被創(chuàng)建。這就是“延遲實(shí)例化(Lazy Instance)”。 *單件模式的應(yīng)用場(chǎng)景之一是設(shè)置類(lèi)對(duì)象,比如注冊(cè)表設(shè)置(Register Setting)對(duì)象。如果設(shè)置對(duì)象有多份拷貝,就會(huì)把設(shè)置搞得一團(tuán)糟。 *單件常用來(lái)管理共享的資源,比如數(shù)據(jù)庫(kù)連接池或線程池。 單件(Singleton)模式:確保一個(gè)類(lèi)只有一個(gè)實(shí)例,并提供一個(gè)全局訪問(wèn)點(diǎn)。 *多線程會(huì)影響到單件模式,如果不對(duì)它進(jìn)行處理就會(huì)在單件模式下仍然創(chuàng)建多于一個(gè)實(shí)例。 解決這個(gè)問(wèn)題有以下三種方式: (1)使用同步。但是簡(jiǎn)單地給創(chuàng)建實(shí)例方法(getInstance())增加synchronized修飾符雖然可以解決多線程的問(wèn)題,但是導(dǎo)致每次調(diào)用都同步,而在靜態(tài)變量被設(shè)置之后,同步就是多余的了,因此,這降低了程序的效率。 在程序頻繁運(yùn)行的地方增加同步可能會(huì)使效率降低100倍!因此要盡量避免使用同步,如果使用,就要盡量縮減需要同步的代碼。 方法如下:
------------ public class Singleton { private static Singleton instance; private Singleton() {} public synchronized static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } ------------ (2)使用“急切(eagerly)”創(chuàng)建實(shí)例,也就是在生命靜態(tài)變量的時(shí)候就創(chuàng)建實(shí)例,而不是等到使用的時(shí)候再創(chuàng)建。該方式適用于程序總是需要?jiǎng)?chuàng)建和使用單件實(shí)例、程序在創(chuàng)建和運(yùn)行時(shí)負(fù)擔(dān)不是太重、單件實(shí)例占用的資源較少的情況。 方法如下:
------------ public class Singleton { private static Singleton instance = new Singleton(); private Singleton() {} public static Singleton getInstance() { return instance; } } ------------ (3)在創(chuàng)建實(shí)例方法(getInstance())方法中使用“雙重檢查加鎖(Double-Checked Locking)”,這樣既保持了“延遲實(shí)例化(Lazy Instance)”,又保證只在第一次調(diào)用時(shí)同步。 方法如下:
------------ public class Singleton { private volatile static Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } } ------------ 在這個(gè)方法里使用到了volatile這個(gè)關(guān)鍵字,下面對(duì)這個(gè)“關(guān)鍵的”關(guān)鍵字進(jìn)行說(shuō)明: Java語(yǔ)言規(guī)范指出,為了獲得最佳速度,允許線程保存共享成員變量的私有拷貝,而且只當(dāng)線程進(jìn)入和離開(kāi)同步代碼塊時(shí)才與共享成員變量的原始值進(jìn)行對(duì)比。 而被volatile修飾的成員變量在線程中的私有拷貝每次被線程訪問(wèn)時(shí),都強(qiáng)迫從共享內(nèi)存中重讀該成員變量的值。而且,當(dāng)成員變量的私有拷貝發(fā)生變化時(shí),強(qiáng)迫線程將變化值回寫(xiě)到共享內(nèi)存。這樣在任何時(shí)刻,兩個(gè)不同的線程總是看到某個(gè)成員變量的同一個(gè)值。 因此volatile關(guān)鍵字是使“雙重檢查加鎖(Double-Checked Locking)”有效的前提。 但是需要注意,在1.4及以前版本的JDK中,JVM對(duì)volatile關(guān)鍵字的實(shí)現(xiàn)會(huì)導(dǎo)致雙重檢查加鎖失效,所以這個(gè)方法只適用于1.5(包含)以后版本。 *以上對(duì)三種處理多線程方法的總結(jié)也就是“Sharpen Your Pencil”的解答。 *可以通過(guò)把一個(gè)類(lèi)中的全部方法都定義為靜態(tài)方法的方式來(lái)達(dá)到和單件模式同樣的效果,但是由于類(lèi)的初始化順序由JVM控制,所以可能導(dǎo)致與初始化順序有關(guān)的BUG,而這樣的BUG常常難于被發(fā)現(xiàn)。當(dāng)類(lèi)的初始化比較簡(jiǎn)單時(shí),可以使用此方法。 *類(lèi)加載器會(huì)破壞單件模式,因?yàn)椴煌念?lèi)加載器可以分別創(chuàng)建同一個(gè)單件的對(duì)象,單件對(duì)象就有了多個(gè)實(shí)例。解決辦法是:自行指定類(lèi)加載器,并且指定同一個(gè)類(lèi)加載器。 *在Java 1.2及以前版本中,垃圾收集器有一個(gè)BUG,會(huì)造成單件在沒(méi)有全局引用時(shí),被當(dāng)做垃圾清理掉。在1.2以后的版本中,這個(gè)BUG已經(jīng)得到了修復(fù),因此不用擔(dān)心了。 如果使用的是1.2及以前的版本,可以建立一個(gè)單件注冊(cè)表(Register),從而避免單件被垃圾收集器回收。 *雖然單件模式不支持繼承,但在一個(gè)軟件中用到它的機(jī)會(huì)并不多,所以這個(gè)限制幾乎沒(méi)有影響。 *Java中實(shí)現(xiàn)單件(Singleton)模式需要私有的構(gòu)造器、一個(gè)靜態(tài)變量和一個(gè)靜態(tài)方法。 2.單件(Singleton)模式實(shí)例 第一種實(shí)現(xiàn):
public class ThreadPool { private static ThreadPool instance; private ThreadPool() { } public synchronized static ThreadPool getInstance() { if (instance == null) { instance = new ThreadPool(); } return instance; } } 第二種實(shí)現(xiàn):
public class DBConnectionPool { private static DBConnectionPool instance = new DBConnectionPool(); private DBConnectionPool() { } public static DBConnectionPool getInstance() { return instance; } } 第三種實(shí)現(xiàn)(適用于1.5及以后版本):
public class LogFactory { private volatile static LogFactory instance; private LogFactory() { } public static LogFactory getInstance() { if (instance == null) { synchronized (LogFactory.class) { if (instance == null) { instance = new LogFactory(); } } } return instance; } } --END-- |
|
來(lái)自: 昵稱(chēng)465563 > 《我的圖書(shū)館》