一、Lock體系
1. ReentrantLock(可重入鎖)
Lock lock = new ReentrantLock();
lock.lock();
try {// 臨界區代碼
} finally {lock.unlock();
}
- 特點:可重入、支持公平/非公平策略
- 優勢:可中斷鎖獲取、定時鎖等待
- 使用場景:替代synchronized需要更靈活控制的場景
1. 核心使用場景
(1) 需要可中斷的鎖獲取
ReentrantLock lock = new ReentrantLock();try {// 支持響應中斷的鎖獲取lock.lockInterruptibly();try {// 執行可能長時間運行的任務processCriticalSection();} finally {lock.unlock();}
} catch (InterruptedException e) {// 處理中斷邏輯handleInterruption();
}
典型場景:
- 實現可取消任務
- 處理死鎖恢復機制
- 響應式系統中斷處理
(2) 精確超時控制
if (lock.tryLock(300, TimeUnit.MILLISECONDS)) {try {// 臨界區操作} finally {lock.unlock();}
} else {// 執行替代邏輯fallbackOperation();
}
適用場景:
- 高并發系統的熔斷機制
- 實時系統的時間敏感操作
- 分布式鎖的本地模擬
(3) 公平鎖需求
ReentrantLock fairLock = new ReentrantLock(true); // 公平鎖public void fairAccess() {fairLock.lock();try {// 保證先到先得的訪問順序} finally {fairLock.unlock();}
}
適用場景:
- 交易撮合系統
- 訂單處理隊列
- 需要嚴格順序執行的批處理
(4) 多條件變量
class BoundedBuffer {final Lock lock = new ReentrantLock();final Condition notFull = lock.newCondition();final Condition notEmpty = lock.newCondition();public void put(Object x) throws InterruptedException {lock.lock();try {while (count == items.length)notFull.await();// ... put logicnotEmpty.signal();} finally {lock.unlock();}}
}
典型應用:
- 生產者-消費者模式
- 阻塞隊列實現
- 復雜狀態管理
6. 常見誤區
誤區 1:認為 ReentrantLock 總是比 synchronized 快
- 在低競爭場景使用 synchronized
誤區 2:忘記在 finally 中釋放鎖
總結建議
-
虛擬線程優先策略:
- 新項目直接使用 ReentrantLock
- 舊系統逐步替換關鍵路徑的 synchronized
-
鎖選擇決策樹:
if (需要可中斷/超時 || 需要公平性 || 虛擬線程環境)→ 選擇 ReentrantLock else if (簡單同步 && 短期持有)→ 使用 synchronized else→ 評估其他并發工具(如 StampedLock)
-
監控指標:
- 鎖等待時間(超過 10ms 需要告警)
- 虛擬線程固定率(目標 < 5%)
- 鎖競爭頻率(每秒競爭次數)
通過深入理解 ReentrantLock 的機制和虛擬線程的協同工作原理,開發者可以構建出更高性能、更易維護的并發系統。在實際項目中,建議結合 APM 工具(如 Micrometer)持續監控鎖使用情況,實現動態調優。
2. ReentrantReadWriteLock(讀寫鎖)
ReadWriteLock rwLock = new ReentrantReadWriteLock();
Lock readLock = rwLock.readLock();
Lock writeLock = rwLock.writeLock();
- 讀寫分離:共享讀鎖(允許多線程并發讀),獨占寫鎖
- 鎖降級機制:寫鎖可降級為讀鎖
- 適用場景:讀多寫少的數據結構(如緩存)
3. StampedLock(郵戳鎖)
StampedLock stampedLock = new StampedLock();
long stamp = stampedLock.tryOptimisticRead();
// 讀取共享變量
if (!stampedLock.validate(stamp)) {// 升級為悲觀讀stamp = stampedLock.readLock();// ...stampedLock.unlockRead(stamp);
}
- 三種模式:寫鎖、悲觀讀、樂觀讀
- 無重入特性,需防止死鎖
- 性能優勢:樂觀讀不阻塞寫操作
4. LockSupport(線程阻塞工具)
Thread thread = new Thread(() -> {LockSupport.park();// 被喚醒后執行
});
thread.start();
LockSupport.unpark(thread);
- 基于許可證的線程控制
- 精準喚醒指定線程
- 底層Unsafe類實現
5. SpinLock(自旋鎖實現示例)
public class SpinLock {private AtomicBoolean locked = new AtomicBoolean(false);public void lock() {while (!locked.compareAndSet(false, true)) {// 自旋等待}}public void unlock() {locked.set(false);}
}
- 適用場景:臨界區代碼執行時間極短
- 優點:避免線程上下文切換
- 缺點:CPU空轉消耗資源
6. Condition(條件變量)
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();// 等待方
lock.lock();
try {condition.await();
} finally {lock.unlock();
}// 通知方
lock.lock();
try {condition.signal();
} finally {lock.unlock();
}
- 實現精準的線程等待/通知機制
- 支持多個等待隊列
- 典型應用:阻塞隊列實現
二、并發工具類
1. AbstractQueuedSynchronizer(AQS)
// 自定義同步器示例
class Mutex extends AbstractQueuedSynchronizer {protected boolean tryAcquire(int acquires) {return compareAndSetState(0, 1);}protected boolean tryRelease(int releases) {setState(0);return true;}
}
- CLH隊列管理等待線程
- 模板方法設計模式
- 同步狀態原子管理
2. CountDownLatch(倒計時門閂)
CountDownLatch latch = new CountDownLatch(3);// 工作線程
new Thread(() -> {// 完成任務latch.countDown();
}).start();// 主線程等待
latch.await();
- 一次性使用
- 典型應用:并行任務初始化
3. CyclicBarrier(循環屏障)
CyclicBarrier barrier = new CyclicBarrier(3, () -> {// 所有線程到達后執行
});new Thread(() -> {// 執行任務barrier.await();
}).start();
- 可重復使用
- 支持屏障動作
- 應用場景:多階段并行計算
4. Exchanger(數據交換器)
Exchanger<String> exchanger = new Exchanger<>();new Thread(() -> {String data = exchanger.exchange("Thread1 Data");
}).start();new Thread(() -> {String data = exchanger.exchange("Thread2 Data");
}).start();
- 雙線程數據交換
- 支持超時機制
- 應用場景:管道式處理
5. Phaser(階段同步器)
Phaser phaser = new Phaser(3);new Thread(() -> {phaser.arriveAndAwaitAdvance();// 階段1任務phaser.arriveAndDeregister();
}).start();
- 動態注冊機制
- 分階段任務控制
- 支持分層結構
6. Semaphore(信號量)
// 創建包含3個許可的信號量(公平模式)
Semaphore semaphore = new Semaphore(3, true);// 獲取許可(阻塞方式)
semaphore.acquire();
try {// 訪問共享資源(最多3個線程并發)
} finally {semaphore.release();
}// 非阻塞嘗試獲取
if (semaphore.tryAcquire(1, TimeUnit.SECONDS)) {try {// 臨界區操作} finally {semaphore.release();}
}
核心特性:
-
資源池管理:
- 通過許可(permits)控制并發訪問數量
- 許可數量可以動態調整(
reducePermits
/increasePermits
)
-
靈活獲取方式:
- 支持批量獲取(
acquire(int permits)
) - 提供可中斷/不可中斷獲取方式
- 支持超時機制(
tryAcquire
)
- 支持批量獲取(
-
公平性選擇:
- 非公平模式(默認):吞吐量優先
- 公平模式:按請求順序分配許可
典型應用場景:
- 連接池限流:
// 數據庫連接池實現
public class ConnectionPool {private final Semaphore semaphore;private final BlockingQueue<Connection> pool;public ConnectionPool(int size) {this.semaphore = new Semaphore(size);this.pool = new ArrayBlockingQueue<>(size);// 初始化連接...}public Connection getConnection() throws InterruptedException {semaphore.acquire();return pool.take();}public void release(Connection conn) {pool.offer(conn);semaphore.release();}
}
- 限流控制系統:
// API限流控制器(每秒10個請求)
class RateLimiter {private final Semaphore semaphore = new Semaphore(10);private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);public RateLimiter() {scheduler.scheduleAtFixedRate(() -> {int available = semaphore.availablePermits();if (available < 10) {semaphore.release(10 - available);}}, 0, 1, TimeUnit.SECONDS);}public boolean tryAcquire() {return semaphore.tryAcquire();}
}
- 生產者-消費者模型:
// 有界緩沖區實現
class BoundedBuffer<E> {private final Semaphore availableItems;private final Semaphore availableSpaces;private final Queue<E> queue = new LinkedList<>();public BoundedBuffer(int capacity) {availableItems = new Semaphore(0);availableSpaces = new Semaphore(capacity);}public void put(E item) throws InterruptedException {availableSpaces.acquire();synchronized (this) {queue.offer(item);}availableItems.release();}public E take() throws InterruptedException {availableItems.acquire();E item;synchronized (this) {item = queue.poll();}availableSpaces.release();return item;}
}
使用技巧:
- 許可動態調整:
// 運行時動態擴展容量
void resize(int newCapacity) {int delta = newCapacity - semaphore.availablePermits();if (delta > 0) {semaphore.release(delta); // 擴容} else {semaphore.reducePermits(-delta); // 縮容}
}
- 死鎖預防:
// 使用tryAcquire避免死鎖
if (semaphore.tryAcquire(2, 100, TimeUnit.MILLISECONDS)) {try {// 臨界區操作} finally {semaphore.release(2);}
} else {// 處理獲取失敗邏輯
}
- 與ReentrantLock對比:
特性 | Semaphore | ReentrantLock |
---|---|---|
資源控制 | 多許可控制 | 單鎖獨占 |
可重入性 | 不支持 | 支持 |
公平性 | 可配置 | 可配置 |
條件變量 | 無 | 支持Condition |
使用場景 | 資源池/流量控制 | 互斥操作 |
注意事項:
-
釋放次數匹配:
- 確保acquire()與release()調用次數匹配
- 使用try-finally保證釋放
-
不可重入特性:
- 同一線程多次acquire需要對應次數的release
-
性能監控:
- 通過availablePermits()監控系統負載
- 使用getQueueLength()檢測等待線程數
-
資源泄漏預防:
- 建議使用帶超時的tryAcquire
- 結合Thread.interrupt()處理阻塞線程
源碼關鍵實現:
// 基于AQS的Sync內部類
abstract static class Sync extends AbstractQueuedSynchronizer {Sync(int permits) {setState(permits);}final int getPermits() {return getState();}final int nonfairTryAcquireShared(int acquires) {for (;;) {int available = getState();int remaining = available - acquires;if (remaining < 0 ||compareAndSetState(available, remaining))return remaining;}}protected final boolean tryReleaseShared(int releases) {for (;;) {int current = getState();int next = current + releases;if (next < current) // overflowthrow new Error("Maximum permit count exceeded");if (compareAndSetState(current, next))return true;}}
}
五、最佳實踐(補充)
- Semaphore使用準則:
- 許可數量設置應基于系統壓測結果
- 避免在持有信號量時執行阻塞操作
- 對不可靠資源訪問添加finally釋放塊
- 結合監控系統實現動態配額調整
通過Semaphore的合理使用,可以有效解決資源池管理、流量控制、系統過載保護等典型并發問題。其靈活的許可管理機制使其成為構建高彈性系統的利器,但需注意避免因錯誤使用導致的線程饑餓或系統死鎖問題。
三、高性能計數器
1. LongAdder
LongAdder counter = new LongAdder();
counter.increment();
long sum = counter.sum();
- 實現原理:
- 基礎值 + Cell數組分散競爭
- 最終一致性保證
- 適用場景:高頻寫、低頻讀
2. DoubleAdder
DoubleAdder adder = new DoubleAdder();
adder.add(1.5);
double sum = adder.sum();
- 類似LongAdder的浮點版本
- 注意精度問題
- 適用場景:統計指標收集
3. 性能對比
計數器類型 | 寫性能 | 讀性能 | 內存消耗 | 適用場景 |
---|---|---|---|---|
AtomicLong | 低 | 高 | 低 | 讀寫平衡場景 |
LongAdder | 高 | 低 | 高 | 寫多讀少場景 |
四、選型策略
- 鎖機制選擇:
- 優先考慮synchronized
- 需要高級功能時選擇ReentrantLock
- 讀多寫少場景使用StampedLock
- 并發工具選擇:
- 一次性等待用CountDownLatch
- 多階段任務用Phaser
- 線程數固定用CyclicBarrier
- 計數器選擇:
- 普通場景使用AtomicLong
- 高并發寫場景使用LongAdder
- 需要精確值時慎用DoubleAdder
五、最佳實踐
- 避免鎖嵌套使用
- 總是使用try-finally釋放鎖
- 合理設置超時時間
- 優先使用并發集合類
- 使用ThreadLocal減少競爭
- 監控鎖競爭情況(JStack、JConsole)
本文深入剖析了Java并發編程的核心組件,開發者應根據具體場景選擇最合適的工具。在高并發系統中,合理的鎖選擇和并發工具使用可以帶來數量級的性能提升。建議結合JVM參數調優(如偏向鎖、自旋參數)和并發監控工具進行綜合優化。