核心
保證一個(gè)類只有一個(gè)對(duì)象,并且提供一個(gè)訪問(wèn)該類的全局訪問(wèn)點(diǎn)。
優(yōu)點(diǎn)
- 減少系統(tǒng)性能開(kāi)銷,,對(duì)象的產(chǎn)生需要較多資源時(shí),如配置文件讀取,產(chǎn)生其他依賴對(duì)象,可在應(yīng)用啟動(dòng)時(shí)直接產(chǎn)生一個(gè)單例對(duì)象,然后駐留內(nèi)存。
- 可以優(yōu)化共享資源的訪問(wèn),如可以用一個(gè)單例類負(fù)責(zé)所有數(shù)據(jù)表的映射處理。
常見(jiàn)的五種實(shí)現(xiàn)方式
餓漢式(線程安全,調(diào)用效率高,但不能延時(shí)加載)
public class SingletonDemo {
// 類初始化時(shí),立即加載這個(gè)類,沒(méi)有延時(shí)加載,線程安全
private static SingletonDemo instance = new SingletonDemo();
private SingletonDemo() {
}
public static SingletonDemo getIntance() {
return instance;
}
}
static變量在類裝載時(shí)初始化,虛擬機(jī)保證只會(huì)裝載一次類,肯定不會(huì)發(fā)生并發(fā)訪問(wèn)問(wèn)題。
問(wèn)題:若只是想加載類,而不是調(diào)用getInstance(),甚至永遠(yuǎn)沒(méi)調(diào)用,就會(huì)造成資源的浪費(fèi)。
懶漢式(線程安全,調(diào)用效率不高,可延時(shí)加載)
public class SingletonDemo {
// 延時(shí)加載,真正調(diào)用時(shí)才創(chuàng)建對(duì)象
private static SingletonDemo instance;
private SingletonDemo() {
}
public static synchronized SingletonDemo getIntance() {
if(instance == null) {
instance = new SingletonDemo();
}
return instance;
}
}
延遲加載,真正調(diào)用時(shí)才加載。資源利用率高。
問(wèn)題:每次調(diào)用getInstance() 都要同步,并發(fā)效率低。
雙重檢測(cè)鎖實(shí)現(xiàn)(JVM內(nèi)部模型原因,有時(shí)會(huì)有問(wèn)題,不建議使用)
public class SingletonDemo {
private static SingletonDemo instance = null;
private SingletonDemo() {
}
public static SingletonDemo getIntance() {
if(instance == null) {
SingletonDemo sc;
synchronized (SingletonDemo.class) {
sc = instance;
if(sc == null) {
synchronized (SingletonDemo.class) {
if(sc == null) {
sc = new SingletonDemo();
}
}
instance = sc;
}
}
}
return instance;
}
}
將同步內(nèi)容放到if內(nèi)部,不必每次調(diào)用時(shí)都同步,只有第一次才同步,提高了執(zhí)行效率。
問(wèn)題:由于編譯器優(yōu)化原因和JVM底層內(nèi)部模型原因,有時(shí)會(huì)出現(xiàn)問(wèn)題,不建議使用
靜態(tài)內(nèi)部類式(線程安全,調(diào)用效率高,且可延時(shí)加載)
public class SingletonDemo {
private SingletonDemo() {
}
private static class SingletonClassInstance {
private static final SingletonDemo instance = new SingletonDemo();
}
private static SingletonDemo getInstance() {
return SingletonClassInstance.instance;
}
}
- 外部類沒(méi)有static屬性,則不會(huì)像餓漢式那樣立即加載對(duì)象。
- 只有真正調(diào)用getInstance(),才會(huì)加載靜態(tài)內(nèi)部類。加載類時(shí)是線程 安全的。 instance是static final
類型,保證了內(nèi)存中只有這樣一個(gè)實(shí)例存在,而且只能被賦值一次,從而保證了線程安全性. - 兼?zhèn)淞瞬l(fā)高效調(diào)用和延遲加載的優(yōu)勢(shì)!
枚舉單例(線程安全,調(diào)用效率高,但不能延時(shí)加載)
public enum SingletonDemo {
INSTANCE; // 代表一個(gè)實(shí)例
/**
* 根據(jù)需要添加功能處理
*/
public void singletonOperation() {
}
}
優(yōu)點(diǎn):
- 實(shí)現(xiàn)簡(jiǎn)單
- 枚舉本身就是單例模式。由JVM從根本上提供保障!避免通過(guò)反射和反序列化的漏洞!
缺點(diǎn):不能延時(shí)加載
如何選擇
- 對(duì)于單例對(duì)象需要占用資源少的,不需要延時(shí)加載,可選用枚舉式和餓漢式,但枚舉式優(yōu)于餓漢式
- 單例對(duì)象占用資源大的,需要延時(shí)加載的,可選用靜態(tài)內(nèi)部類式和懶漢式,且靜態(tài)內(nèi)部類式優(yōu)于懶漢式。
防止反射和反序列化破解單例模式
反射和反序列化可以破解上述幾種實(shí)現(xiàn)方式,除了枚舉式外。下面是反破解方法
import java.io.ObjectStreamException;
public class SingletonDemo {
private static SingletonDemo instance;
/**
* 防止反射破解單例模式
*/
private SingletonDemo() {
if(instance != null) {
throw new RuntimeException();
}
}
public static synchronized SingletonDemo getInstance() {
if(instance == null) {
instance = new SingletonDemo();
}
return instance;
}
/**
* 防止反序列化破解單例模式
*/
public Object readResolve() throws ObjectStreamException{
return instance;
}
}