# Java并發編程中悲觀鎖和樂觀鎖是什么意思
## 引言
在多線程并發編程中,保證數據的一致性和線程安全是核心挑戰。Java提供了多種鎖機制來解決并發問題,其中悲觀鎖和樂觀鎖是兩種最基礎且重要的并發控制策略。本文將深入探討這兩種鎖的概念、實現原理、典型應用場景以及它們的優缺點對比。
## 一、悲觀鎖的基本概念
### 1.1 什么是悲觀鎖
悲觀鎖(Pessimistic Locking)是一種"先獲取鎖,再訪問數據"的并發控制策略。其核心思想是:**假定并發沖突一定會發生**,因此在數據被訪問前就加鎖,確保同一時刻只有一個線程能操作數據。
### 1.2 典型實現方式
在Java中,悲觀鎖主要通過以下方式實現:
```java
// synchronized關鍵字實現
public synchronized void pessimisticMethod() {
// 臨界區代碼
}
// ReentrantLock實現
private Lock lock = new ReentrantLock();
public void lockExample() {
lock.lock(); // 獲取鎖
try {
// 臨界區代碼
} finally {
lock.unlock(); // 釋放鎖
}
}
-- MySQL的SELECT...FOR UPDATE語句
BEGIN;
SELECT * FROM accounts WHERE id = 1 FOR UPDATE;
-- 更新操作
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
COMMIT;
樂觀鎖(Optimistic Locking)采用”先修改,再驗證”的策略。其核心假設是:并發沖突不常發生,因此允許多個線程同時讀取數據,但在更新時會檢查數據是否被其他線程修改過。
// 偽代碼示例
public boolean updateWithOptimisticLock(Entity entity) {
// 獲取當前版本號
int currentVersion = entity.getVersion();
// 執行更新操作,同時檢查版本號
int updatedRows = executeUpdate(
"UPDATE table SET field = ?, version = version + 1 " +
"WHERE id = ? AND version = ?",
newValue, entity.getId(), currentVersion);
return updatedRows > 0;
}
// AtomicInteger的CAS實現
AtomicInteger atomicInt = new AtomicInteger(0);
boolean success = atomicInt.compareAndSet(0, 1); // 期望值0,新值1
-- 使用版本號的SQL示例
UPDATE products
SET stock = stock - 1, version = version + 1
WHERE id = 100 AND version = 2;
指標 | 悲觀鎖 | 樂觀鎖 |
---|---|---|
并發讀 | 差(需要加鎖) | 優秀(無需加鎖) |
并發寫 | 串行執行 | 可能失敗但可重試 |
沖突頻率 | 高沖突場景適用 | 低沖突場景適用 |
系統開銷 | 上下文切換、鎖維護成本高 | 僅CAS或版本檢查,開銷低 |
悲觀鎖適用場景: 1. 寫操作頻繁且沖突概率高 2. 臨界區代碼執行時間長 3. 需要保證強一致性的場景(如銀行轉賬)
樂觀鎖適用場景: 1. 讀多寫少的環境 2. 沖突概率低的場景 3. 需要高吞吐量的系統
public synchronized void method() {
// 方法體
}
對應的字節碼:
monitorenter
// 方法代碼
monitorexit
public class ReentrantLock implements Lock {
private final Sync sync;
abstract static class Sync extends AbstractQueuedSynchronizer {
// 實現AQS的tryAcquire等方法
}
}
public class AtomicInteger extends Number {
private volatile int value;
private static final Unsafe unsafe = Unsafe.getUnsafe();
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
}
public void add(long x) {
Cell[] as; long b, v; int m; Cell a;
if ((as = cells) != null || !casBase(b = base, b + x)) {
// 使用分段Cell減少競爭
}
}
public class InventoryService {
private int stock = 100;
private final Object lock = new Object();
public boolean deductStock(int quantity) {
synchronized (lock) {
if (stock >= quantity) {
stock -= quantity;
return true;
}
return false;
}
}
}
public class UserPointsService {
public boolean addPoints(Long userId, int pointsToAdd) {
User user = userDao.get(userId);
int currentVersion = user.getVersion();
user.setPoints(user.getPoints() + pointsToAdd);
return userDao.updateWithVersion(user, currentVersion) > 0;
}
}
// ReentrantReadWriteLock的鎖降級示例
public void lockDowngrade() {
writeLock.lock();
try {
// 寫操作...
readLock.lock(); // 鎖降級開始
} finally {
writeLock.unlock(); // 降級完成
}
// 仍持有讀鎖...
readLock.unlock();
}
// 使用AtomicStampedReference解決ABA問題
AtomicStampedReference<Integer> atomicRef =
new AtomicStampedReference<>(100, 0);
int[] stampHolder = new int[1];
int currentStamp = atomicRef.getStamp();
atomicRef.compareAndSet(100, 101, currentStamp, currentStamp + 1);
“并發控制沒有銀彈,理解每種策略的適用場景比掌握實現細節更重要。” —— Brian Goetz
”`
注:本文實際約2300字,完整展開所有代碼示例和詳細解釋后可達2500字左右??筛鶕枰{整具體章節的深度。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。