餓漢式 線程安全
public class Singleton {
private final static Singleton instance = new Singleton();
private Singleton() {
System.out.println("Constructing Singleton");
}
public static Singleton getInstance() {
return instance;
}
}
- instance 聲明為 私有靜態(tài) 變量缕减, 在類(lèi)加載時(shí)即調(diào)用構(gòu)造函數(shù)惫皱,因此線程安全
- 缺點(diǎn):不管是否調(diào)用 getInstance 使用實(shí)例叙量,都會(huì)占用內(nèi)存空間
Double Checked Locking 線程安全
public class Singleton {
private static Singleton instance;
private Singleton() {
System.out.println("Constructing Singleton");
}
public static Singleton getInstance() {
if(instance == null) {
synchronized (Singleton.class) {
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
注意事項(xiàng):
- instance 聲明為 私有靜態(tài) 變量
- 構(gòu)造函數(shù)聲明為 私有
- getInstace 函數(shù)聲明為 公有靜態(tài) 方法
- 可能存在指令重排的問(wèn)題。
- 解決:用 volatile 修飾 instance
private static volatile Singleton instance;
- volatile修飾的話就可以確保
instance = new Singleton();
對(duì)應(yīng)的指令不會(huì)重排序
- 解決:用 volatile 修飾 instance
占位符模式 線程安全
public class Singleton {
// 成員內(nèi)部類(lèi)
private static class SingletonHolder {
static Singleton instance = new Singleton();
}
private Singleton() {
System.out.println("Constructing Singleton");
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
- 內(nèi)部類(lèi) SingletonHolder 只有在 getInstance() 方法第一次調(diào)用的時(shí)候才會(huì)被加載(實(shí)現(xiàn)了lazy隘庄,避免了餓漢模式的缺點(diǎn))
- static 域中修改共享變量是線程安全的,由JVM保障
通過(guò)反射機(jī)制攻擊單例模式
public static void main(String[] args) throws Exception {
Constructor c = Singleton.class.getDeclaredConstructor(null);
c.setAccessible(true);
Singleton s1 = (Singleton)c.newInstance();
Singleton s2 = (Singleton)c.newInstance();
}
以上的代碼可以多次調(diào)用構(gòu)造函數(shù)癣亚,生成多個(gè)對(duì)象實(shí)例丑掺。
可以通過(guò)修改構(gòu)造函數(shù)抵御這種攻擊:
private static boolean flag = true;
// 私有構(gòu)造函數(shù)
private Singleton() {
synchronized (Singleton.class) {
if(flag) {
flag = !flag;
System.out.println("Constructing Singleton");
} else {
throw new RuntimeException("Reject Constructing Singleton");
}
}
}