JAVA设计模式--辛格尔顿
Singleton模式可以作為一種編程技術,讓我們先從理論上說代碼
單例模式三個關鍵點:
1)、某個類僅僅能有一個實例
2)、該類必須自行創建這個實例
3)、該類必須自行向整個系統提供這個實例
應用場景:
1)、window的任務管理器就是非常典型的單例模式,你肯定不能同一時候打開兩個任務管理器
2)、數據庫連接池技術一般採用的都是單例模式。由于數據庫連接是一種數據庫資源。系統中使用數據庫連接池,主要是節省打開或者關閉數據庫連接所引起的
效率損耗。這樣的效率上的損耗還是很昂貴的,用單例模式來維護。就能夠大大減少這樣的損耗。
3)、我們在進行開發時對于配置文件的讀取一般也是採用單例模式,由于配置文件里的內容是全局共享的資源。
4)、多線程的線程池設計一般也要考慮單例模式。線程池能方便對池中線程的控制。
5)、站點的統計類信息。一般也是採用單例模式,否則難以同步控制。比如統計我的博客的訪問量。
6)、我們開發應用程序的日志功能也是採用的單例模式,由于我們僅僅能有一個實例去追加日志信息,否則不好控制。
單例模式的幾類寫法:
1)餓漢式模式
怎么理解呢,餓了嗎。所以我們做飯要很著急,這里也就是說。當類被載入的時候該類就已經將自己的實例創建出來了。
這也是空間換取時間的典型應用,怎么說呢? 我們在類載入的時候就實例化了這個對象。占用了內存空間,可是我們在用到這個對象的時候就不用去實例化了,
直接拿去用就能夠了。也就節省可時間,這也就是空間換取時間。
ps:記得第一份工作(還在大三的時候)面試的時候面試官就讓我舉出我做過的項目中時間換取空間和空間換取時間的典型應用,給我問懵了。
代碼:
package chc.singleton; /*** 餓漢式單例模式類* @author haicheng.cao* @time 2014.09.02 22:40*/ public class EagerSingleton {//類載入的時候就創建了自身的實例對象--餓漢式(空間換取時間)public static EagerSingleton eagerSingleton=new EagerSingleton();/*** 顯示的私有構造方法,防止其它類創建本類實例*/private EagerSingleton(){}/*** 靜態工廠方法,其它類通過調用該方法來獲取本類的實例對象*/public static EagerSingleton getEagerSingleton(){return eagerSingleton;} }2)懶漢式模式
這個怎么理解呢?懶人嗎,舉個樣例。一個人立即要去面試的時候才開始寫簡歷,就是說對象實例要用的時候才去創建。在這里也就是說在類載入的時候并沒有創
建本類的實例對象,而是在其它類在第一次調用的時候才去創建。
這也是典型的時間換取空間的應用,就是嘛,類載入的時候沒有創建對象。節省了內存。也就是節省了空間,調用的時候須要推斷一下這個類的實例對象是否存
在,浪費了時間,這也就是時間換取空間。
代碼:
package chc.singleton; /*** 懶漢式單例模式類* @author haicheng.cao* @time 2014.09.02 23:05*/ public class LazySingleton {//類載入的時候并沒有創建自身實例化對象public static LazySingleton lazySingleton=null;/*** 顯示的私有構造方法。防止其它類創建本類實例*/private LazySingleton(){}/*** 靜態工廠方法,其它類通過調用該方法來獲取本類的實例對象*/public static synchronized LazySingleton getLazySingleton(){//第一次被調用的時候創建自身實例對象if(lazySingleton==null){lazySingleton=new LazySingleton();}return lazySingleton;} } 注意:我們給getEasySingleton()方法加了同步keyword,能夠保證線程安全。可是這樣做比較影響程序的性能,我們能不能改善一下呢?看以下的:3)雙重檢查加鎖
雙重檢查加鎖機制的意思就是:我們在調用getEasySingleton()方法的時候不同步,進入方法內我們推斷一下實例對象是否存在。假設不存在我們在進入同步代
碼塊。這是第一重檢查,進入同步塊后再進行推斷,推斷實例是否存在,假設不存在再創建這個對象的實例。這就是第二重檢查。
這樣。就僅僅有第一次調用的時候
運行了一次同步代碼塊,其余的時候就不須要同步了,提升了程序的性能。
代碼:
package chc.singleton; /*** 雙重檢查加鎖。針對懶漢式提升性能* @author haicheng.cao* @time 2014.09.02 22:40*/ public class TwoCheck {private volatile static TwoCheck twoCheck = null;private TwoCheck(){}public static TwoCheck getInstance(){//先檢查實例是否存在,假設不存在才進入以下的同步塊if(twoCheck == null){//同步塊,線程安全的創建實例synchronized (TwoCheck.class) {//再次檢查實例是否存在,假設不存在才真正的創建實例if(twoCheck == null){twoCheck = new TwoCheck();}}}return twoCheck;} }-------------------------------------以下續寫與2014.09.03 21:15-----------------------------------------------------
昨天在寫這個東西的時候一直在糾結一個問題,如果有一部分全局共享的變量,我們能夠通過在類中聲明靜態屬性。然后通過靜態方法來初始化聲明的那些屬性。
然后這些靜態的變量在不論什么一個類中都能夠被調用了。就像以下這種代碼:
package chc.statics; public class StaticDemo {public String logPath=null;public void init(){logPath="c://log.txt";} } 這種代碼不是全然能夠替代單例的功能嗎?今天上班問了下領導。給我解釋的非常清楚,類中聲明靜態變量的方式的確能夠實現單例模式的功能??墒?#xff0c;上面代碼那種方式你須要在項目啟動的時候調用一下
StaticDemo類的init()方法。單例模式就是全然由自身去維護自己,不須要借助外力。
另一種情況:就是當有些全局屬性是動態變化的時候,那么對于靜態變量的方式就須要程序不斷的去操作該類的動態屬性,而靜態類能夠靈活的自己控制。解除
了代碼的耦合。
package chc.singleton; import java.io.*; public class Singleton {public static File file=null;public static long lastModified; private static Singleton s=null;private Singleton(){lastModified=file.lastModified();}public synchronized static Singleton getSingleton() {//假設變量lastModified的值與文件最后一次被改動的時間值不同的話,又一次實例化一下if(s==null && lastModified!=file.lastModified() ){s=new Singleton();}return s;} }這個樣例就非常直觀了。類中的lastModified的值是動態的,假設用靜態代碼塊去維護的話。程序在每一次改動這個文件的時候都要調用一次靜態代碼塊又一次初始化一下這個變量,單例中卻能夠自己靈活的進行維護,不須要別的類輔助。
-------------------------------------以下續寫與2014.11.03 20:40-----------------------------------------------------
餓漢式存在著占用資源的問題。懶漢式存在著線程安全的問題,以下看一個巧妙的寫法,將懶漢式與餓漢式的長處集成在了一起。攻克了懶漢式與餓漢式的弊端。
package hirain;import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Properties; /*** 即實現了延遲載入,又線程安全* @author haicheng.cao*/ public class AppConfig5 {//靜態內部類在第一次使用的時候被裝載private static class AppConfig5Holder{ private static AppConfig5 instance = new AppConfig5(); } /*** 定義一個方法來為client提供AppConfig類的實例* @return 一個AppConfig的實例*/public static AppConfig5 getInstance(){return AppConfig5Holder.instance;}/*** 私有化構造方法*/private AppConfig5(){//調用讀取配置文件的方法readConfig();}private String id;private String name;public String getId() {return id;}public void setId(String id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;} /*** 讀取配置文件,把配置文件里的內容讀出來設置到屬性上*/private void readConfig(){Properties p = new Properties(); InputStream in = null;try {in = new BufferedInputStream (new FileInputStream("AppConfig.properties"));p.load(in);//把配置文件里的內容讀出來設置到屬性上this.id = p.getProperty("id");this.name = p.getProperty("name");} catch (IOException e) {System.out.println("裝載配置文件出錯了,詳細堆棧信息例如以下:");e.printStackTrace();}finally{try {in.close();} catch (IOException e) {e.printStackTrace();}}}} 關鍵點:靜態內部類的使用,靜態內部類的靜態變量僅僅有在靜態內部類被使用的時候才會載入中。
版權聲明:本文博主原創文章,博客,未經同意不得轉載。
總結
以上是生活随笔為你收集整理的JAVA设计模式--辛格尔顿的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 不要滥用UNLOGGED table 和
- 下一篇: log_miner操作(日志挖掘)