簡介
- 分類:創建型
- 來源:Gang Of Four
- 意圖:保證類只有一個實例,並提供全局的訪問入口
單例
- 單例模式限制了類的實例化(私有構造器),保證虛擬機中一個類(一份字節碼,一個類文件可以被不同類加載器多次加載,不屬於同一份字節碼,例如tomcat中部署多個相同的應用)只有一個實例。
- 單例實現類必須提供一個獲取實例對象的全局訪問入口
單例實現
多種實現單例的方式,但都具備以下概念。
- 通過私有構造器限制類的初始化
- 通過私有靜態變量保證只有一個實例
- 通過公有靜態方法提供實例的全局訪問接口
餓漢模式
package design.structural.singleton;
/**
* 餓漢模式
* 優點:
* 實現簡單
* 缺點:
* 不能延時加載,即使單例不會被用到頁會被初始化
* 不能進行異常處理
*
* @author faith.huan 2019-12-02 22:11
*/
public class EagerSingleton {
/**
* 立即加載方式 = 餓漢模式
*/
private static EagerSingleton singleton = new EagerSingleton();
/**
* 通過私有構造器,限制類的實例化
*/
private EagerSingleton(){}
/**
* 通過公有靜態方法,提供全局訪問入口
*/
public static EagerSingleton getInstance(){
return singleton;
}
}
餓漢模式-靜態塊
針對餓漢模式無法處理異常缺陷的優化版本,採用靜態塊初始化實例
package design.structural.singleton;
/**
* 餓漢模式-靜態塊
* 優點:
* 實現簡單
* 缺點:
* 不能延時加載,即使單例不會被用到頁會被初始化
*
* @author faith.huan 2019-12-02 22:11
*/
public class EagerStaticBlockSingleton {
/**
* 立即加載方式 = 餓漢模式
*/
private static EagerStaticBlockSingleton singleton ;
static {
try {
singleton = new EagerStaticBlockSingleton();
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 通過私有構造器,限制類的實例化
*/
private EagerStaticBlockSingleton(){}
/**
* 通過公有靜態方法,提供全局訪問入口
*/
public static EagerStaticBlockSingleton getInstance(){
return singleton;
}
}
懶漢模式-非線程安全
package design.structural.singleton;
/**
* 懶漢模式-非線程安全
* 優點
* 延時初始化
* 缺點
* 線程不安全
*
* @author faith.huan 2019-12-02 22:33
*/
public class LazyThreadNotSafeSingleton {
/**
* 延時加載方式 = 懶漢模式
*/
private static LazyThreadNotSafeSingleton singleton;
/**
* 通過私有構造器,限制類的實例化
*/
private LazyThreadNotSafeSingleton(){}
/**
* 通過公有靜態方法,提供全局訪問入口
*/
public static LazyThreadNotSafeSingleton getInstance(){
if(singleton==null){
singleton = new LazyThreadNotSafeSingleton();
}
return singleton;
}
}
懶漢模式-線程安全
解決線程安全最簡單的方式就是在靜態方法上加synchronized
關鍵字
package design.structural.singleton;
/**
* 懶漢模式-線程安全
* 優點
* 延時初始化
* 缺點
* 用於每次獲取對象都需要加鎖,性能較差
*
* @author faith.huan 2019-12-02 22:33
*/
public class LazyThreadSafeSingleton {
/**
* 延時加載方式 = 懶漢模式
*/
private static LazyThreadSafeSingleton singleton;
/**
* 通過私有構造器,限制類的實例化
*/
private LazyThreadSafeSingleton(){}
/**
* 通過公有靜態方法,提供全局訪問入口
*/
public static synchronized LazyThreadSafeSingleton getInstance(){
if(singleton==null){
singleton = new LazyThreadSafeSingleton();
}
return singleton;
}
}
雙檢查鎖模式-DCL
針對懶漢模式-線程安全
版本使用方法級的synchronize
關鍵字來實現線程安全帶來的性能問題,採用Double Check Lock(DCL)方式來優化。
package design.structural.singleton;
/**
* 雙檢查鎖模式-DCL
* 優點
* 延時初始化,線程安全
* 缺點
* JDK1.5及以後版本
*
* @author faith.huan 2019-12-02 22:33
*/
public class DoubleCheckLockSingleton {
/**
* 延時加載方式 = 懶漢模式
*/
private static volatile DoubleCheckLockSingleton singleton;
/**
* 通過私有構造器,限制類的實例化
*/
private DoubleCheckLockSingleton() {
}
/**
* 通過公有靜態方法,提供全局訪問入口
*/
public static DoubleCheckLockSingleton getInstance() {
if (singleton == null) {
synchronized (DoubleCheckLockSingleton.class) {
if (singleton == null) {
singleton = new DoubleCheckLockSingleton();
}
}
}
return singleton;
}
}
要點:
- 靜態變量singleton需使用volatile關鍵字修飾
代碼singleton = new DoubleCheckLockSingleton();會分解爲以下三行僞代碼
1.memory = allocate(); // 1. 分配對象內存空間
2.initInstance(memory); // 2. 初始化對象
3.singleton =memory; // 3.設置singleton變量指向剛分配的內存地址
在不加volatile修飾時,上面僞代碼中2、3步可能會發生重排序,也就是先指向內存地址(此時singleton==null已爲false),再初始化對象。
多線程時序表
時間 | 線程A | 線程B |
---|---|---|
T1 | A1: 分配對象內存空間 | |
T2 | A3: 設置singleton指向剛分配的內存地址 | |
T3 | B1:判斷 singleton是否爲null | |
T4 | B2: 由於singleton不爲null 線程B將訪問singleton對象 |
|
T5 | A2:初始化對象 |
- 此方案需要JDK1.5或更高版本
從JDK1.5開始使用新的JSR-133內存模型規範,此規範增強了volatile的語義,此處使用的是禁止指令重排
靜態內部類
package design.structural.singleton;
/**
* 靜態內部類
* 比爾.普格
*
* @author faith.huan 2019-12-03 22:16
*/
public class BillPughSingleton {
/**
* 通過私有構造器,限制類的實例化
*/
private BillPughSingleton() {
}
/**
* 通過靜態內部類實現單例
* 只有當getInstance方法調用時,SingletonHolder纔會被加載
*/
private static class SingletonHolder {
private static final BillPughSingleton SINGLETON = new BillPughSingleton();
}
/**
* 通過公有靜態方法,提供全局訪問入口
*/
public static BillPughSingleton getInstance() {
return SingletonHolder.SINGLETON;
}
}
枚舉
package design.structural.singleton;
/**
* 枚舉模式
*
* @author faith.huan 2019-12-03 22:26
*/
public enum EnumSingleton {
/**
* 單例對象
*/
SINGLETON;
}