鎖是並發編程中協調共享資源訪問的核心手段。本文系統梳理 Java 體系中共享鎖/排他鎖、悲觀鎖/樂觀鎖、公平鎖/非公平鎖、可重入鎖、自旋鎖、偏向鎖/輕量級鎖/重量級鎖等核心概念,並對比其適用場景與實現原理。
| 鎖類型 | 別名 | 兼容性 | 操作權限 | 典型場景 |
|---|---|---|---|---|
| 共享鎖 (S鎖) | 讀鎖 | S-S 兼容,S-X 互斥 | 只能讀,不能改 | SELECT ... LOCK IN SHARE MODE |
| 排他鎖 (X鎖) | 寫鎖 | X 與任何鎖互斥 | 可讀可改 | SELECT ... FOR UPDATE、UPDATE/DELETE |
意向鎖 (IS/IX):表級鎖,標明事務意圖獲取行級 S/X 鎖,避免全表掃描檢查行鎖衝突。
| 維度 | 悲觀鎖 | 樂觀鎖 |
|---|---|---|
| 核心假設 | 衝突常見,先獲取鎖再操作 | 衝突罕見,操作後驗證衝突 |
| 實現方式 | synchronized、Lock、數據庫行鎖 | CAS(Compare-And-Swap)、版本號/時間戳 |
| 優點 | 強一致性、簡單直接 | 無阻塞、高並發吞吐、無死鎖 |
| 缺點 | 阻塞等待、死鎖風險、性能差 | ABA 問題、衝突重試開銷、只能保證單變量原子性 |
| 適用場景 | 寫多讀少、強一致性要求、臨界區耗時長 | 讀多寫少、衝突率低、CAS 硬件支持 |
// CAS 核心:compareAndSet(expected, update)
// 底層依賴 Unsafe.compareAndSwapInt/Long/Object + volatile
AtomicInteger count = new AtomicInteger(0);
count.incrementAndGet(); // 線程安全計數
// 高並發計數器:LongAdder 分段累加,最後求和
LongAdder adder = new LongAdder();
adder.increment();
long sum = adder.sum();
ReentrantLock(true)。優點:無飢餓;缺點:吞吐率低,線程切換開銷大synchronized、ReentrantLock() 默認。優點:吞吐率高;缺點:可能飢餓選擇建議:默認非公平鎖;只有明確公平性需求(如嚴格順序處理)才用公平鎖。
synchronized、ReentrantLock 均為可重入鎖| 鎖狀態 | 觸發條件 | 核心機制 | 適用場景 | Mark Word 狀態 |
|---|---|---|---|---|
| 無鎖 | 對象創建 | 無同步 | 單線程訪問 | 01 (無鎖標識) |
| 偏向鎖 | 單線程反復訪問 | CAS 設置線程 ID 到 Mark Word,後續無需 CAS | 無競爭、單線程高頻 | 101 (偏向標識) + ThreadID |
| 輕量級鎖 | 多線程交替訪問 | CAS 將 Mark Word 指向棧中 Lock Record,自旋等待 | 低競爭、短臨界區 | 00 (輕量級標識) + Lock Record 指針 |
| 重量級鎖 | 高競爭/自旋超時 | ObjectMonitor 監視器,OS 互斥量,線程阻塞 | 高競爭、長臨界區 | 10 (重量級標識) + Monitor 指針 |
關鍵參數:-XX:+UseBiasedLocking (默認開啟)、-XX:BiasedLockingStartupDelay=4000、-XX:PreBlockSpin=10 (自旋次數)。
// 內核自旋鎖簡化模型
spinlock_t lock = SPIN_LOCK_UNLOCKED;
void spin_lock(spinlock_t *lock) {
while (test_and_set_bit(0, &lock->lock)) {
cpu_relax(); // PAUSE 指令,降低總線壓力
}
// 獲得鎖,進入臨界區
}
void spin_unlock(spinlock_t *lock) {
clear_bit(0, &lock->lock);
}
| 接口/類 | 核心特性 | 關鍵方法 |
|---|---|---|
| Lock | 顯式獲取/釋放、可中斷、超時、公平性選項 | lock(), unlock(), tryLock(), lockInterruptibly() |
| ReentrantLock | 可重入、支持 Condition、公平/非公平 | 繼承 Lock,newCondition() |
| ReadWriteLock | 讀寫分離、多讀單寫 | readLock(), writeLock() |
| ReentrantReadWriteLock | 可重入讀寫鎖、支持降級(寫→讀) | 實現 ReadWriteLock |
| StampedLock | 樂觀讀鎖、不可重入、性能更強 | tryOptimisticRead(), validate(), readLock(), writeLock() |
jstack 線程導出分析 BLOCKED 狀態、Arthas thread -b、JConsole/JVisualVM 死鎖檢測