標籤:開發 設計 模式 執行個體 設計模式
單例模式是開發模式中最簡單,最易於理解的一種模式。簡單地說,它指的就是始終保持一個執行個體的意思。但是,Java的類是可以穿件多個執行個體的,那麼,怎麼實現呢?
顧名思義,單例模式就是只有一個執行個體。單例模式確保某一個類只有一個執行個體,這個類稱為單例類,單例模式有3個要點:
①是某個類只能有一個執行個體;
②它必須自行建立這個執行個體;
③是它必須自行向整個系統提供這個執行個體。例如,一些資源管理員常常設計成單例模式。
在電腦系統中,需要管理的資源有很多,例如每台電腦可以有若干個印表機,但只能有一個列印控制器,以避免兩個列印工作同時輸出到印表機中,每台電腦可以有若干傳真卡,但是只應該有一個軟體負責管理傳真卡,以避免出現兩份傳真作業同時傳到傳真卡的情況。
總之,選擇單例模式就是為了避免不一致狀態。
首先看一個經典的單例模式實現:
public class Singleton { private static Singleton uniqueInstance = null; private Singleton() { // Exists only to defeat instantiation. } public static Singleton getInstance() { if (uniqueInstance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; } }
Singleton通過將構造方法限定為private避免了類在外部被執行個體化,在同一個虛擬機器範圍內,Singleton的唯一執行個體只能通過getInstance()方法訪問。(事實上,通過Java反射機制是能夠執行個體化構造方法為private的類的,那基本上會使所有的Java單例實現失效。此問題在此處不做討論,姑且掩耳盜鈴地認為反射機制不存在。)
但是以上實現沒有考慮線程的安全性,所謂執行緒安全性,就是如果你的代碼所在的進程裡有多個線程在同時運行,而這些線程可能同時會運行這段代碼,如果每次啟動並執行結果和單線程啟動並執行結果一致,而且他們的變數的值也和預期的是一樣的,就是安全執行緒的。或者說:一個類或者程式所提供的介面對於線程來說是原子操作或者多個線程之間的切換不會導致該介面的執行結果存在二義性,也就是說我們不用考慮同步的問題。顯然以上實現並不滿足安全執行緒的要求,在並發環境下很可能出現多個Singleton執行個體。
Java中實現單例模式一般要注意以下幾點:
- 私人的構造方法,保證外部無法建立類執行個體。
- 私人的靜態類型引用。因為靜態就可以保證只有一個變數引用。
- 提供擷取執行個體的方法。方法名一般為getInstance()。
單例模式通常有兩種實現方式:懶漢式和餓漢式
1,懶漢式
對於懶漢式來說,一般需要在getInstance()方法中首先判斷執行個體是否為空白,也就是第一次訪問的時候才會進入該if語句,然後再返回該執行個體,例如下面的代碼:
private static lanhanObject obj = null;public static lanhanObject getInstance(){ if(obj==null) obj = new lanhanObject();//建立新的 return obj;}
懶漢式有一個缺點,他可能會使線程不安全而無法保證100%的單例,例如,當線程A在以上代碼的第4行(進入if語句,建立執行個體之前)暫停了,此時線程B進入了執行個體。因此,為了保證懶漢式能做到100%的單例,還需要為getInstance( )方法加上synchronized關鍵字以保證線程同步,例如如下的代碼:
public static synchronized lanhanObject getInstance(){//擷取執行個體方法,保證同步 if(obj == null){ obj = new lanhanObject(); } return obj;}
synchronized 關鍵字也可以運用在方法體中,用同步代碼塊的形式來保證線程同步,不一定用同步方法。
2,餓漢式
對於餓漢式來說,需要做就是把new 語句寫在引用變數定義的地方,然後getInstance( )直接返回就可以了,例如下面的代碼:
private static ehanObject obj = new ehanObject(); //靜態類型引用public static ehanObject getInstance(){ //擷取執行個體方法 return obj;}
餓漢式不存線上程安全的問題,但是他可能會造成資源浪費的情況。因為,執行個體會在類載入的時候,隨著靜態變數的初始化而建立,但是有的時候並不會使用該執行個體,那麼它的建立就有一些浪費了,如果執行個體比較龐大的話,會影響程式的效能。
總之,懶漢式和餓漢式沒有絕對的優劣,主要 根據具體的情況來決定。
package com.leetch.java//餓漢式 單例 優點:實現簡單;缺點:在不需要的時候,白建立了對象,造成資源的浪費class ConnectionPoolA { private static ConnectionPoolA cPoolA = new ConnectionPoolA(); // 建立執行個體 private ConnectionPoolA() {} //私人構造方法 public static ConnectionPoolA getConnectionPoolA() { return cPoolA; }}//懶漢式 單例 優點:需要對象的時候才建立;缺點:線程不安全 class ConnectionPoolB{ private static ConnectionPoolB cPoolB; private ConnectionPoolB(){} public static synchronized ConnectionPoolB getconneConnectionPoolB(){ if(cPoolB==null){ cPoolB = new ConnectionPoolB(); } return cPoolB; }}public class SinglObjectParttern{ public static void main(String args[]){ ConnectionPoolA cp = ConnectionPoolA.getConnectionPoolA(); //建立1 ConnectionPoolB cp2 = ConnectionPoolB.getconneConnectionPoolB(); //建立2 System.out.println(cp == cp2); }}
因為單例模式下,得到的始終是同一個對象,因此,以上代碼的運行結果是 :
true
[Java設計模式](一)怎樣實現Singleton(單例)模式編程