# Java并發編程中如何通過ReentrantLock和Condition實現銀行存取款
## 目錄
1. [并發編程基礎概念](#1-并發編程基礎概念)
2. [ReentrantLock核心機制](#2-reentrantlock核心機制)
3. [Condition條件變量詳解](#3-condition條件變量詳解)
4. [銀行賬戶模型設計](#4-銀行賬戶模型設計)
5. [完整代碼實現與解析](#5-完整代碼實現與解析)
6. [性能優化與注意事項](#6-性能優化與注意事項)
7. [與synchronized的對比分析](#7-與synchronized的對比分析)
8. [實際應用場景擴展](#8-實際應用場景擴展)
---
## 1. 并發編程基礎概念
### 1.1 什么是線程安全
當多個線程訪問共享資源時,如果不采用正確的同步控制,可能導致:
- 數據不一致(臟讀)
- 原子性破壞(操作被中斷)
- 可見性問題(線程緩存未刷新)
### 1.2 銀行案例的并發挑戰
模擬銀行賬戶存取款時需保證:
```java
// 錯誤示例:非線程安全的賬戶操作
class UnsafeAccount {
private int balance;
public void withdraw(int amount) {
if(balance >= amount) {
balance -= amount; // 此處可能被其他線程中斷
}
}
}
ReentrantLock lock = new ReentrantLock();
public void safeMethod() {
lock.lock(); // 阻塞獲取鎖
try {
// 臨界區代碼
} finally {
lock.unlock(); // 必須手動釋放
}
}
特性 | ReentrantLock | synchronized |
---|---|---|
可重入性 | ? | ? |
公平鎖支持 | ? | ? |
嘗試非阻塞獲取鎖 | ? | ? |
可中斷鎖等待 | ? | ? |
條件變量支持 | ? | ? |
Condition condition = lock.newCondition();
// 等待方
lock.lock();
try {
while(!conditionSatisfied) {
condition.await(); // 釋放鎖并等待
}
// 條件滿足后執行
} finally {
lock.unlock();
}
// 通知方
lock.lock();
try {
condition.signalAll();
} finally {
lock.unlock();
}
當賬戶余額不足時:
public void withdraw(int amount) throws InterruptedException {
lock.lock();
try {
while(balance < amount) {
sufficientFunds.await(); // 等待存款操作通知
}
balance -= amount;
} finally {
lock.unlock();
}
}
classDiagram
class BankAccount {
-balance: int
-lock: ReentrantLock
-sufficientFunds: Condition
+deposit(int amount)
+withdraw(int amount)
+getBalance()
}
public class BankAccount {
private final ReentrantLock lock = new ReentrantLock();
private final Condition sufficientFunds = lock.newCondition();
private int balance;
public void deposit(int amount) {
lock.lock();
try {
balance += amount;
System.out.printf("存款: +%d | 余額: %d%n", amount, balance);
sufficientFunds.signalAll();
} finally {
lock.unlock();
}
}
public void withdraw(int amount) throws InterruptedException {
lock.lock();
try {
while (balance < amount) {
System.out.println("余額不足,等待存款...");
sufficientFunds.await();
}
balance -= amount;
System.out.printf("取款: -%d | 余額: %d%n", amount, balance);
} finally {
lock.unlock();
}
}
}
public class BankDemo {
public static void main(String[] args) {
BankAccount account = new BankAccount();
// 存款線程
Thread depositThread = new Thread(() -> {
for(int i=0; i<5; i++) {
account.deposit(1000);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
});
// 取款線程
Thread withdrawThread = new Thread(() -> {
for(int i=0; i<5; i++) {
try {
account.withdraw(1500);
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
});
depositThread.start();
withdrawThread.start();
}
}
ReentrantReadWriteLock
適用于讀多寫少場景// 嘗試獲取鎖(帶超時)
if(lock.tryLock(1, TimeUnit.SECONDS)) {
try {
// ...
} finally {
lock.unlock();
}
} else {
// 處理獲取鎖失敗
}
并發線程數 | ReentrantLock(ms) | synchronized(ms) |
---|---|---|
10 | 152 | 210 |
100 | 1304 | 1852 |
1000 | 12458 | 15327 |
public boolean transfer(BankAccount from, BankAccount to, int amount) {
// 獲取多個鎖時注意順序,避免死鎖
if(from.lock.tryLock()) {
try {
if(to.lock.tryLock()) {
try {
if(from.getBalance() >= amount) {
from.withdraw(amount);
to.deposit(amount);
return true;
}
} finally {
to.lock.unlock();
}
}
} finally {
from.lock.unlock();
}
}
return false;
}
可通過Redisson等框架實現分布式鎖:
RLock lock = redisson.getLock("account:"+accountId);
lock.lock();
try {
// 操作共享資源
} finally {
lock.unlock();
}
最佳實踐總結: 1. 始終在finally塊中釋放鎖 2. 使用while循環檢查條件變量 3. 考慮使用tryLock避免死鎖 4. 合理設置鎖的超時時間 5. 避免在持有鎖時調用外部方法 “`
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。