單例模式
單例模式(Singleton Pattern)是一種常用的軟件設計模式尖啡。在它的核心結(jié)構(gòu)中只包含一個被稱為單例類的特殊類。通過單例模式可以保證系統(tǒng)中一個類只有一個實例而且該實例易于外界訪問冗锁,從而方便對實例個數(shù)的控制并節(jié)約系統(tǒng)資源。
- 某個類只能有一個實例。
- 它必須自行創(chuàng)建這個實例痛倚。
- 它必須自行向整個系統(tǒng)提供這個實例殿较。
單例模式關鍵點:
- 構(gòu)造函數(shù)私有化(不讓外部創(chuàng)建此類對象)耸峭。
- 通過一個靜態(tài)方法或枚舉返回單例類對象。
- 多線程環(huán)境下斜脂,確保單例(難點)抓艳。
- 反序列化不會重新構(gòu)建對象。
餓漢式
public class Singleton {
// 單例對象
private final static Singleton INSTANCE = new Singleton();
// 或
// private final static Singleton INSTANCE;
// static {
// INSTANCE = new Singleton();
// }
// 私有構(gòu)造方法
private Singleton() {
}
// 公有靜態(tài)方法用于獲取單例對象
public static Singleton getInstance() {
return INSTANCE;
}
}
線程安全帚戳。但是沒使用它時玷或,就已經(jīng)建立了對象,開銷內(nèi)存片任。
懶漢式
public class Singleton {
// 單例對象
private static Singleton INSTANCE = null;
// 私有構(gòu)造方法
private Singleton() {
}
// synchronized關鍵字修飾靜態(tài)方法
public static synchronized Singleton getInstance() {
if (null == INSTANCE) {// 空值判斷
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
在第一次調(diào)用getInstance()
時偏友,創(chuàng)建單例對象。synchronized
關鍵字修飾对供,使getInstance()
是一個同步方法位他。保證多線程情況下單例對象的唯一氛濒。但是,上鎖會耗費資源鹅髓。
雙檢鎖式(Double Check Lock(DCL))
public class Singleton {
// 單例對象舞竿,volatile關鍵字保證INSTANCE的可見性
private volatile static Singleton INSTANCE = null;
// 私有構(gòu)造方法
private Singleton() {
}
// 兩次空值檢查,synchronized關鍵字同步
public static Singleton getInstance() {
if (null == INSTANCE) {
synchronized (Singleton.class) {
if (null == INSTANCE) {
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
語句INSTANCE = new Singleton();
在編譯時的步驟如下:
- 給Singleton的實例分配內(nèi)存窿冯。
- 調(diào)用Singleton()的構(gòu)造函數(shù)骗奖,初始化成員字段。
- 將INSTANCE 對象指向分配的內(nèi)存空間(此時對象非空)醒串。
Java編譯器允許處理器亂序執(zhí)行执桌。上訴的2,3兩步的順序可能被打亂芜赌。打亂后仰挣,非空判斷的線程不安全。在JDK1.5之后缠沈,調(diào)整了JVM膘壶,具體化了volatile
關鍵字,表示修飾的變量線程可見博烂。使用volatile
關鍵字會影響性能香椎。
靜態(tài)內(nèi)部類
public class Singleton {
// 內(nèi)部類,包含單例的對象
private static class SingletonHolder {
public static final Singleton INSTANCE = new Singleton();
}
// 私有構(gòu)造方法
private Singleton() {
}
// 公有靜態(tài)方法用于獲取單例對象
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
第一次加載Singleton
時禽篱,SingletonHolder
不會被加載畜伐。只用調(diào)用getInstance()
時,才加載SingletonHolder
躺率,并創(chuàng)建INSTANCE
玛界。推薦大家使用。
枚舉式
public enum SingletonEnum {
INSTANCE;
}
雖說代碼簡單悼吱,enum
的實質(zhì)也是class
(編譯時慎框,會先翻譯成一個class)。優(yōu)點是反序列化也不會重新生成新的實例后添。
前幾種方式笨枯,如果要避免反序列化也不會重新生成新的實例。需要重寫:
private Object readResolve() throws ObjectStreamException{
return INSTANCE;
}
單例管理類
一個程序可能有多個單例對象遇西。先建立一個上訴管理類馅精。
public class SingletonManager {
// 容器
private static Map<String, Object> objMap = new HashMap<String, Object>();
// 私有構(gòu)造方法
private SingletonManager() {
}
// 注冊單例
public static void ascendSingleton(String key, Object instance) {
if (!objMap.containsKey(key)) {
objMap.put(key, instance);
}
}
// 獲得單例對象
public static Object getInstance(String key) {
return objMap.get(key);
}
}
通過注冊,可以管理多種類型的單例粱檀,使用統(tǒng)一接口洲敢,隱藏具體實現(xiàn)。
使用時:
//注冊單例
SingletonManager.ascendSingleton("SingletonKey",Singleton.getInstance());
//獲得單例對象
Singleton singleton = SingletonManager.getInstance("SingletonKey");
優(yōu)點
- 只有一個對象茄蚯,全局使用压彭。
- 只需要建立一次睦优,占一個內(nèi)存,節(jié)約資源壮不。
缺點
- 沒有接口汗盘,難以擴展。
- 生命周期長忆畅,如果引用短生命周期對象會內(nèi)存泄漏衡未。
補充
通過Java反射機制是能夠?qū)嵗瘶?gòu)造方法為private的類的,那基本上會使所有的Java單例實現(xiàn)失效家凯。
附件
設計模式Demo
GitHub源碼:https://github.com/wzmyyj/Design-Pattern