什麼是單例模式?
概念
單例模式是一種常見的設計模式,可以使得某個類只有一個例項,自己建立該類例項並向外界提供該例項。
單例的三要素:
- 私有構造器(防止客戶端例項化)
- 靜態屬性
- 靜態方法(同於獲取單例例項)
單例模式優缺點
優點
- 資料共享
- 專案安全(配合同步實現)
缺點
- 併發性降低
- 增加耦合
為什麼使用單例模式?
在某些條件下,使用單例模式可以控制類例項的數量,達到節約資源的目的,同時由於只有一個例項,可以實現通訊的功能以及資源共享,不僅如此,單例模式配合執行緒的同步可解決併發訪問中的安全問題。
在一些優先考慮安全性的程式中可以嘗試使用單例。
單例模式的缺點是非常明顯的,降低程式的併發性,增加模組之間的耦合,所以單例的使用需要謹慎。
幾種實現方式
根據例項建立的時機可以分為兩類:lazy-load、非lazy-load。
非lazy-load
/**
* 單例模式
* 當類載入時建立單例例項
*
* 優點:不需要考慮併發
* 缺點:類載入後不獲取單例物件會造成記憶體浪費
*/
public final class IvoryTower {
/**
* 私有構造器
*/
private IvoryTower() {}
/**
* 靜態屬性
*/
private static final IvoryTower INSTANCE = new IvoryTower();
/**
* 靜態方法
*
* @return 單例例項
*/
public static IvoryTower getInstance() {
return INSTANCE;
}
}
複製程式碼
非lazy-load
執行緒安全的單例類
/**
* 執行緒安全的單例類
* 例項是惰性初始化的,因此需要同步
*
* 注意:如果通過反射建立,那麼在同一個類載入器中不會建立單例物件,而是多個例項
*/
public final class ThreadSafeLazyLoadedIvoryTower {
private static ThreadSafeLazyLoadedIvoryTower instance;
private ThreadSafeLazyLoadedIvoryTower() {
// 防止通過反射例項化
if (instance == null) {
instance = this;
} else {
throw new IllegalStateException("Already initialized.");
}
}
/**
* 只有在第一次呼叫例項時才會建立例項。延遲載入
*/
public static synchronized ThreadSafeLazyLoadedIvoryTower getInstance() {
if (instance == null) {
instance = new ThreadSafeLazyLoadedIvoryTower();
}
return instance;
}
}
複製程式碼
靜態內部類
/**
* 利用靜態內部類實現lazy-load的單例模式
*/
public final class InitializingOnDemandHolderIdiom {
/**
* 私有構造器
*/
private InitializingOnDemandHolderIdiom() {}
/**
* 靜態方法
*
* @return 單例例項
*/
public static InitializingOnDemandHolderIdiom getInstance() {
return HelperHolder.INSTANCE;
}
/**
* 靜態內部類
*/
private static class HelperHolder {
private static final InitializingOnDemandHolderIdiom INSTANCE =
new InitializingOnDemandHolderIdiom();
}
}
複製程式碼
Double check locking
/**
* Double check locking
*/
public final class ThreadSafeDoubleCheckLocking {
private static volatile ThreadSafeDoubleCheckLocking instance;
/**
* 私有構造器
*/
private ThreadSafeDoubleCheckLocking() {
// 防止通過反射例項化
if (instance != null) {
throw new IllegalStateException("Already initialized.");
}
}
/**
* 靜態方法
*
* @return 單例例項
*/
public static ThreadSafeDoubleCheckLocking getInstance() {
// 區域性變數可以提高25%的效能
// Joshua Bloch,《Effective Java,第二版》,第283-284頁
ThreadSafeDoubleCheckLocking result = instance;
if (result == null) {
synchronized (ThreadSafeDoubleCheckLocking.class) {
result = instance;
if (result == null) {
instance = result = new ThreadSafeDoubleCheckLocking();
}
}
}
return result;
}
}
複製程式碼