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