單例模式最初的定義出現於《設計模式》(艾迪生維斯理, 1994):“保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。” Java中單例模式定義:“一個類有且僅有一個實例,並且自行實例化向整個系統提供。”
單例對象(Singleton)是一種使用頻率較高的設計模式。在JAVA APP中單例能確保JVM中只有一個存在的實例。這種設計思想有以下一些利點:
1. 一些常用的類的頻繁創建,且系統開銷較大的類,降低其對系統的開銷。
2. 限制了new 操作符的使用,降低內存使用次數,減輕系統垃圾回收的壓力。
3. 對於某些核心操作,例如數據存儲,付費交易,單實例才能保證對整個流程的準確,完全的控制。
單例模式的基本是,私有構造方法,讓類不可被外部實例化 ,並持有一個私有(不可以被外部引用)的靜態實例,只提供一個公有的靜態方法給外部調用,用於實例創建。
下面是一個簡單的單例類
代碼:
public class DataBserHelp { private static DataBserHelp dataBserHelp = null; private DataBserHelp() { } public static DataBserHelp getInstance() { if (null == dataBserHelp) { dataBserHelp = new DataBserHelp(); } return dataBserHelp; } }
測試類使用:
public class WorkClass { public void test() { DataBserHelp dataBserHelp = DataBserHelp.getInstance();
dataBserHelp.query("select * from table where ...");}}
在不考慮線程安全的情況下,這樣的單例完全可以滿足需求。但如果我們把它放入多線程的環境下,肯定就會出現問題了。解決這個問題的方法,首先我們會想到對getInstance方法加同步鎖即synchronized關鍵字。
Code:
public static synchronized DataBserHelp getInstance() { if (null == dataBserHelp) { dataBserHelp = new DataBserHelp(); } return dataBserHelp; }
synchronized關鍵字鎖住的是這個對象,這樣的用法,在性能上會有所下降,因爲每次調用getInstance(),都要對對象上鎖,事實上,只有在第一次創建對象的時候需要加鎖,之後就不需要了,所以,這個地方需要繼續改進Code:
public class DataBserHelp { private static DataBserHelp dataBserHelp = null; private DataBserHelp() { } public static DataBserHelp getInstance() { if (null == dataBserHelp) { syncInit(); } return dataBserHelp; } private static synchronized void syncInit() { if (null == dataBserHelp) { dataBserHelp = new DataBserHelp(); } } }整個程序只需要創建一次實例,所以性能上也不會有什麼影響。以上代碼基本可以說解決了單例模式的線程安全。
單例模式中一般還會有一種情況需要注意,常言道優點也即缺點,單實例對於有需求屬性更新時,是個問題,這裏引出影子實例的概念,即直接生成另一個單例對象實例,這個新生成的單例對象實例將從數據庫或文件中讀取最新的配置信息;然後將這些配置信息直接賦值給舊單例對象的屬性。
Code如下
public class DataBserHelp { private static DataBserHelp dataBserHelp = null; public Vector getmProperties() { return mProperties; } public void updateProperties() { DataBserHelp shadow = new DataBserHelp();//新生成的對象將從數據庫或者文件中讀取最新的配置信息,再賦值給舊單例對象 mProperties = shadow.getmProperties(); } private Vector mProperties = null; private DataBserHelp() { mProperties = new Vector(); if (null == dataBserHelp) { mProperties.add(0, "1.0"); } else { mProperties.add(0, "2.0"); } } public static DataBserHelp getInstance() { if (null == dataBserHelp) { syncInit(); } return dataBserHelp; } private static synchronized void syncInit() { if (null == dataBserHelp) { dataBserHelp = new DataBserHelp(); } } }
測試類代碼:
public class WorkClass { public void test() { DataBserHelp dataBserHelp = DataBserHelp.getInstance(); System.out.println("dataBserHelp.getmProperties().get(0)=="+dataBserHelp.getmProperties().get(0)); dataBserHelp.updateProperties(); System.out.println("dataBserHelp.getmProperties().get(0)=="+dataBserHelp.getmProperties().get(0)); } }
輸出:
I/System.out: dataBserHelp.getmProperties().get(0)==1.0 I/System.out: dataBserHelp.getmProperties().get(0)==2.0
結語:單例模式簡單易懂,但是實際項目中的運用還是有一定的深度的,synchronized關鍵字鎖定的是對象,在用的時候,一定要在恰當的地方使用,可能並不是整個對象或某個過過程都需要鎖。影子實例的運用切實解決了單例對象的屬性更新需求。
與單例類相似功能的靜態類與之有什麼區別?總結如下:
1. 靜態類不能實現接口,單例可以。
2. 靜態類一般首次加載初始化,而單例可以延遲初始化,這點對於提升性能有幫助。
3. 單例類可以被繼承,方法可覆寫,靜態類方法都是static的,無法覆寫。