public class SingletonV1 {
private static SingletonV1 instance;
private SingletonV1() {}
public static SingletonV1 getInstance() {
if (instance == null) instance = new SingletonV1();
return instance;
}
}
缺陷:多線程並發進入 if 導致多次實例化。
final int CONCURRENT = 10;
ExecutorService pool = Executors.newCachedThreadPool();
CountDownLatch latch = new CountDownLatch(CONCURRENT);
for (int i = 0; i < CONCURRENT; i++) {
pool.execute(() -> {
latch.countDown();
try { latch.await(); } catch (InterruptedException ignored) {}
SingletonV1.getInstance(); // 並發調用
});
}
latch.await(); pool.shutdown();
// 控制台可能打印多次 "實例化" 日誌
public static synchronized SingletonV2 getInstance() { ... }
每次調用加鎖,高並發下嚴重爭用。
public class SingletonDCL {
// volatile 禁止指令重排,保證其他線程看到完全初始化的對象
private static volatile SingletonDCL instance;
private SingletonDCL() {}
public static SingletonDCL getInstance() {
if (instance == null) { // 第一次檢查 (無鎖,性能)
synchronized (SingletonDCL.class) { // 類級別鎖
if (instance == null) { // 第二次檢查 (鎖內)
instance = new SingletonDCL();
}
}
}
return instance;
}
}
關鍵點:volatile 防止 instance = new SingletonDCL() 的指令重排 (分配內存 → 初始化對象 → 賦值引用),避免其他線程看到「半初始化」對象。
public class SingletonHolder {
private SingletonHolder() {}
private static class Holder {
static final SingletonHolder INSTANCE = new SingletonHolder();
}
public static SingletonHolder getInstance() {
return Holder.INSTANCE;
}
}
原理:類加載機制保證 Holder 類首次被主動使用時才初始化,且初始化過程由 JVM 鎖同步,天然線程安全、無鎖開銷、代碼簡潔。
public enum SingletonEnum {
INSTANCE;
public void doSomething() { ... }
}
優勢:防反射破解、防反序列化重新創建、語法最簡潔、JVM 保證單例。
| 場景 | 推薦版本 |
|---|---|
| 一般業務、需延遲加載 | 靜態內部類 (版本 4) |
| 需防反射/序列化攻擊 | 枚舉 (版本 5) |
| 需手動控制實例化時機/參數 | DCL (版本 3) |
| 簡單工具類、無狀態 | 餓漢式 (類加載時創建) |