當 JDK 內建同步工具(synchronized、ReentrantLock、Semaphore、CountDownLatch 等)無法滿足特定同步語義時,需基於 AbstractQueuedSynchronizer (AQS) 或 Condition 構建自定義同步器。
acquire lock on object state; // 獲取鎖
while (precondition does not hold) { // 條件不滿足
release lock; // 釋放鎖
wait until precondition might hold; // 等待條件
optionally fail if interrupted or timeout; // 中斷/超時處理
reacquire lock; // 重新獲取鎖
}
perform action; // 執行動作
release lock; // 釋放鎖
public class BaseBoundedBuffer<V> {
private final V[] buf;
private int tail, head, count;
@SuppressWarnings("unchecked")
public BaseBoundedBuffer(int capacity) {
this.buf = (V[]) new Object[capacity];
}
// 核心操作(已同步)
public synchronized void doPut(V v) {
buf[tail] = v;
if (++tail == buf.length) tail = 0;
count++;
}
public synchronized V doTake() {
V v = buf[head];
if (++head == buf.length) head = 0;
count--;
return v;
}
public final synchronized boolean isFull() { return count == buf.length; }
public final synchronized boolean isEmpty() { return count == 0; }
}
public synchronized void put1(V v) throws Exception {
if (isFull()) throw new Exception("Buffer full");
doPut(v);
}
缺點:異常用於控制流、調用者需處理失敗、未解決等待問題。
public void put2(V v) throws InterruptedException {
while (true) {
synchronized (this) {
if (!isFull()) { doPut(v); return; }
}
Thread.sleep(SLEEP_TIME); // 難以調優:太短佔 CPU,太長響應慢
}
}
public synchronized void put3(V v) throws InterruptedException {
while (isFull()) wait(); // 釋放鎖並進入等待隊列
doPut(v);
notifyAll(); // 喚醒所有等待線程
}
優點:高效阻塞、自動釋放鎖、JVM 級調度、響應及時。
void stateDependentMethod() throws InterruptedException {
synchronized(lock) {
while (!conditionPredicate) lock.wait(); // 必須用 while!
// 執行受保護動作
}
// 執行完畢後通知
synchronized(lock) { lock.notifyAll(); }
}
比內置監視器更靈活:每個 Lock 可有多個 Condition、可中斷等待、超時等待、公平/非公平隊列。
public class ConditionBoundedBuffer<V> {
private final V[] buf;
private int tail, head, count;
private final Lock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
@SuppressWarnings("unchecked")
public ConditionBoundedBuffer(int capacity) {
this.buf = (V[]) new Object[capacity];
}
public void put(V v) throws InterruptedException {
lock.lock();
try {
while (count == buf.length) notFull.await(); // 等待不滿
buf[tail] = v;
if (++tail == buf.length) tail = 0;
count++;
notEmpty.signal(); // 喚醒消費者
} finally {
lock.unlock();
}
}
public V take() throws InterruptedException {
lock.lock();
try {
while (count == 0) notEmpty.await(); // 等待不空
V v = buf[head];
buf[head] = null; // GC 友好
if (++head == buf.length) head = 0;
count--;
notFull.signal(); // 喚醒生產者
return v;
} finally {
lock.unlock();
}
}
}
| 步驟 | 說明 |
|---|---|
| 1. 定義狀態語義 | int state 代表什麼?(計數、許可、標誌...) |
| 2. 實現 tryAcquire/Release | CAS 修改 state,返回成功/失敗 |
| 3. 實現 isHeldExclusively | Condition 依賴此方法判斷持有權 |
| 4. 暴露同步方法 | acquire/release 等委託給 AQS |
| 5. 提供 Condition | newCondition() 返回 ConditionObject |
優先使用 JDK 內建工具;需自定義時,優先用 Lock + Condition 組合,複雜同步語義再考慮繼承 AQS。核心原則:**條件等待必須在 while 循環中、鎖保護狀態變量、notifyAll 防信號丟失**。