Java 線程模型雖簡化開發,但其內存模型(JMM)與同步機制存在根本缺陷,導致雙重檢查鎖定(DCL)等常見模式失效。本文深度剖析 JMM 架構、DCL 失效雙重根因,並介紹 Doug Lea Concurrent 套件提供的增強同步組件。

read/load(主存→工作存)、use/assign(運算)、store/write(工作存→主存)read+load 刷新工作內存,退出時 store+write 刷回主內存無 synchronized 保護時,JVM 不保證讀寫順序與時機,導致「髒讀」與指令重排。
class Foo {
private Resource res = null;
public Resource getResource() {
if (res == null) { // 檢查 1
synchronized (this) {
if (res == null) // 檢查 2
res = new Resource(); // 初始化
}
}
return res;
}
}
構造函數與賦值可能重排:
// 偽代碼展示重排後順序
Objects[i].reference = allocateMemory(); // 1. 分配記憶體、賦值引用
initializeObject(); // 2. 呼叫構造函數初始化
線程 A 執行至賦值引用但未完成初始化時被切換,線程 B 看到 res != null 直接使用,導致存取未完全初始化的物件。
SMP 架構下,各 CPU 擁有私有快取。線程 A 退出同步區塊寫回主存時,res 引用與 Resource 物件內容寫入順序不定。線程 B 可能先看到引用非空,卻讀到舊的物件內容。

class ResSingleton { public static Resource res = new Resource(); }
private volatile Resource res; 禁止重排,確保可見性套件提供 Sync、Channel、Executor 三大核心介面體系,現已納入 JDK 1.5 (JSR-166)。

Semaphore:資源池管理,支援超時獲取Mutex:非重入互斥鎖,可跨方法傳遞NullSync:空實現,保留內存屏障語義ObservableSync:結合觀察者模式,用於效能調試TimeoutSync:裝飾器模式,添加超時監控
阻塞式 put/take、超時式 offer/poll。實現類:LinkedQueue、BoundedBuffer、SynchronousChannel 等。

public class TestChannel {
final Channel msgQ = new LinkedQueue();
public static void main(String[] args) {
TestChannel tc = new TestChannel();
for (int i = 0; i < 10; i++) {
try { tc.serve(); Thread.sleep(1000); }
catch (InterruptedException ie) {}
}
}
public void serve() throws InterruptedException {
String status = doService();
msgQ.put(status); // 阻塞直到放入
}
private String doService() { return "service completed OK!"; }
public TestChannel() {
Runnable logger = new Runnable() {
public void run() {
try { for (;;) System.out.println("Logger: " + msgQ.take()); }
catch (InterruptedException ie) {}
}
};
new Thread(logger).start();
}
}

PooledExecutor:固定/可變大小線程池QueuedExecutor:任務隊列緩衝FJTaskRunnerGroup:分治任務專用public class TestExecutor {
public static void main(String[] args) {
PooledExecutor pool = new PooledExecutor(new BoundedBuffer(10), 20);
pool.createThreads(4);
try {
ServerSocket socket = new ServerSocket(9999);
for (;;) {
final Socket connection = socket.accept();
pool.execute(new Runnable() {
public void run() { new Handler().process(connection); }
});
}
} catch (Exception e) {}
}
static class Handler { void process(Socket s) {} }
}
volatile 語義增強(JSR-133)可修復 DCL,但理解底層原理仍至關重要wait/notify 手工實現Executor 抽象線程生命週期,Channel 解耦生產消費,Sync 統一同步語義,構建可維護的高併發系統