多線程編程的核心挑戰是共享數據同步與線程協作通信。本文系統講解 synchronized、ReentrantLock、wait/notify、Condition 四大同步原語的語法、內存語義、底層實現、性能特徵,並以生產者-消費者為主線串聯實戰模式,附 JDK 源碼級剖析與避坑指南。
| 維度 | synchronized | ReentrantLock |
|---|---|---|
| 語法 | 關鍵字,隱式獲取/釋放 | API,顯式 lock()/unlock() |
| 可中斷 | 不可中斷(阻塞直到獲取) | lockInterruptibly() 可響應中斷 |
| 超時獲取 | 不支持 | tryLock(timeout) 支持 |
| 公平性 | 非公平 | 可選公平/非公平(構造器參數) |
| 條件變量 | 單一 wait/notify | 多 Condition(精確喚醒) |
| 性能 (JDK 6+) | 偏向鎖/輕量級鎖/重量級鎖自適應優化 | 穩定,無鎖升級開銷 |
| 監控診斷 | JVM 內建,jstack 可見 | 需自行埋點或 JMX |
選型建議:簡單同步用 synchronized(JVM 優化最好、代碼最簡);需中斷/超時/多條件/公平鎖 → ReentrantLock。
// 1. 實例方法 → 鎖當前對象 this
public synchronized void instanceMethod() { }
// 2. 靜態方法 → 鎖當前類 Class 對象
public static synchronized void staticMethod() { }
// 3. 同步塊 → 鎖指定對象(推薦:縮小鎖範圍)
public void blockSync() {
synchronized (this) { /* 臨界區 */ }
// 或專用鎖對象
private final Object lock = new Object();
synchronized (lock) { /* 臨界區 */ }
}
無鎖 → 偏向鎖 (Biased) → 輕量級鎖 → 重量級鎖
(單線程) (CAS) (自旋) (OS Mutex)
• 偏向鎖:首次獲取線程記錄在對象頭,後續同線程無 CAS 開銷。
• 輕量級鎖:多線程交替,CAS 修改對象頭指向棧鎖記錄。
• 重量級鎖:競爭激烈/自旋超限,膨脹為 ObjectMonitor,線程進入 WAITING 狀態。
volatile 語義)。volatile 語義)。ReentrantLock lock = new ReentrantLock(); // 非公平
// ReentrantLock lock = new ReentrantLock(true); // 公平
lock.lock();
try {
// 臨界區
} finally {
lock.unlock(); // 必須在 finally 釋放
}
// 進階能力
lock.lockInterruptibly(); // 可中斷獲取
lock.tryLock(1, TimeUnit.SECONDS); // 超時獲取
Condition notFull = lock.newCondition(); // 多條件變量
Condition notEmpty = lock.newCondition();
synchronized (lock) {
while (conditionNotMet) { // while 防虛假喚醒
lock.wait(); // 釋放鎖,進入 WAITING
}
// 條件滿足,執行任務
lock.notify(); // 喚醒一個等待線程
// lock.notifyAll(); // 喚醒所有(推薦:避免信號丟失)
}
ReentrantLock lock = new ReentrantLock();
Condition notEmpty = lock.newCondition();
Condition notFull = lock.newCondition();
lock.lock();
try {
while (queue.isEmpty()) notEmpty.await(); // 釋放鎖,等待
take();
notFull.signal(); // 精確喚醒「非滿」等待者
} finally { lock.unlock(); }
notifyAll 驚群效應。awaitInterruptibly()。await(timeout) 返回 boolean。class BoundedBuffer{ private final V[] buf; private int putIdx = 0, takeIdx = 0, count = 0; @SuppressWarnings("unchecked") public BoundedBuffer(int capacity) { buf = (V[]) new Object[capacity]; } public synchronized void put(V v) throws InterruptedException { while (count == buf.length) wait(); // 滿等待 buf[putIdx] = v; if (++putIdx == buf.length) putIdx = 0; count++; notifyAll(); // 唤醒消費者 } public synchronized V take() throws InterruptedException { while (count == 0) wait(); // 空等待 V v = buf[takeIdx]; buf[takeIdx] = null; // GC 友好 if (++takeIdx == buf.length) takeIdx = 0; count--; notifyAll(); // 唤醒生產者 return v; } }
class BoundedBufferV2{ private final V[] buf; private int putIdx = 0, takeIdx = 0, count = 0; private final ReentrantLock lock = new ReentrantLock(); private final Condition notFull = lock.newCondition(); private final Condition notEmpty = lock.newCondition(); @SuppressWarnings("unchecked") public BoundedBufferV2(int capacity) { buf = (V[]) new Object[capacity]; } public void put(V v) throws InterruptedException { lock.lockInterruptibly(); try { while (count == buf.length) notFull.await(); buf[putIdx] = v; if (++putIdx == buf.length) putIdx = 0; count++; notEmpty.signal(); // 只喚醒消費者 } finally { lock.unlock(); } } public V take() throws InterruptedException { lock.lockInterruptibly(); try { while (count == 0) notEmpty.await(); V v = buf[takeIdx]; buf[takeIdx] = null; if (++takeIdx == buf.length) takeIdx = 0; count--; notFull.signal(); // 只喚醒生產者 return v; } finally { lock.unlock(); } } }
| 工具 | 場景 | 核心方法 |
|---|---|---|
CountDownLatch | 一組線程等待 N 個事件完成 | countDown()/await() |
CyclicBarrier | N 個線程相互等待彙聚 | await()/reset() |
Semaphore | 限制並發訪問數(許可證) | acquire()/release() |
Phaser | 多階段同步,動態註冊 | arriveAndAwaitAdvance() |
Exchanger | 兩線程交換數據 | exchange(V) |
StampedLock | 讀寫鎖優化,樂觀讀 | tryOptimisticRead()/validate() |