多線程共享可變狀態時,原子性、可見性、有序性三大特性若無保障,必現數據競爭、指令重排、內存可見性失效等並發Bug。Java提供synchronized、volatile、wait/notify、Lock/Condition、原子類等機制。本文深度解析核心同步原語與Java內存模型 (JMM)。
// ① 實例方法鎖:this
public synchronized void instanceMethod() { ... }
// ② 靜態方法鎖:Class對象
public static synchronized void staticMethod() { ... }
// ③ 同步塊:顯式指定鎖對象
public void blockSync(Object lock) {
synchronized (lock) { ... } // 鎖住lock對象
}
| 鎖對象 | 保護範圍 | 典型場景 | 風險 |
|---|---|---|---|
this | 當前實例所有同步方法/塊 | 實例狀態保護 | 外部代碼可鎖住實例導致死鎖/活性失效 |
Class.class | 所有實例共享(類級) | 靜態狀態、單例初始化 | 粒度粗、影響所有實例 |
| 私有final對象 | 僅內部可訪問 | 推薦:private final Object lock = new Object(); | 無外部干擾、封裝性最好 |
| 字符串常量 | JVM全局共享(常量池) | 極度危險 | 無關代碼共用同一鎖 → 嚴重競爭/死鎖 |
| 局部變量new Object() | 每次調用新對象 | 無效 | 每次鎖不同對象,失去互斥 |
| 特性 | 定義 | 保障機制 |
|---|---|---|
| 原子性 | 操作不可分割 | synchronized/Lock/原子類/volatile(僅長整型/雙精度非原子) |
| 可見性 | 一線程寫入,他線程可見 | volatile/synchronized/final/Lock |
| 有序性 | 程序順序與執行順序一致 | volatile/synchronized/final/happens-before規則 |
unlock happens-before 同鎖後續 lockvolatile happens-before 讀同 volatileThread.start() happens-before 線程內任意操作Thread.join() 返回interrupt() happens-before 被中斷線程檢測到中斷final 域 happens-before 對象引用可見編譯器/CPU可重排指令,**但不改變單線程語義**。volatile/synchronized插入內存屏障禁止特定重排:
volatile寫:StoreStore + StoreLoad 屏障volatile讀:LoadLoad + LoadStore 屏障synchronized進/出:全屏障
class VolatileDemo {
volatile boolean stopped = false; // 可見性保證
long counter = 0; // 非volatile,64位非原子!
void run() {
while (!stopped) counter++; // 編譯器可能將stopped讀取提取出循環!volatile阻止
}
void stop() { stopped = true; } // 寫入立即對其他線程可見
}
適用場景:狀態標誌、雙重檢查鎖單例、獨立觀察者模式。**不適用**:複合操作(count++)、不變量維護(需鎖/原子類)。
class ProdConQueue {
private final T[] items;
private int putIdx = 0, takeIdx = 0, count = 0;
private final Object notFull = new Object();
private final Object notEmpty = new Object();
@SuppressWarnings("unchecked")
public ProdConQueue(int capacity) { items = (T[]) new Object[capacity]; }
public void put(T item) throws InterruptedException {
synchronized (notFull) {
while (count == items.length) notFull.wait();
items[putIdx] = item;
putIdx = (putIdx + 1) % items.length;
count++;
synchronized (notEmpty) { notEmpty.notifyAll(); }
}
}
public T take() throws InterruptedException {
synchronized (notEmpty) {
while (count == 0) notEmpty.wait();
T item = items[takeIdx];
items[takeIdx] = null;
takeIdx = (takeIdx + 1) % items.length;
count--;
synchronized (notFull) { notFull.notifyAll(); }
return item;
}
}
}
雙鎖設計:notFull/notEmpty分離生產者/消費者等待集,減少競爭。實際生產用ArrayBlockingQueue/LinkedBlockingQueue。
本文掌握核心同步原語:
下一站:JUC並發包核心——Lock/Condition、AQS原理、原子類、並發集合、線程池、CompletableFuture、虛擬線程。