# Java中如何用一把鎖保護多個資源
## 引言
在多線程編程中,保護共享資源免受并發訪問的破壞是至關重要的。Java提供了多種同步機制,其中`synchronized`關鍵字和`ReentrantLock`是最常用的鎖工具。當我們需要保護多個相關聯的資源時,如何高效地使用同一把鎖就成為了一個關鍵問題。本文將深入探討:
1. 多資源保護的核心挑戰
2. 基于對象鎖的解決方案
3. 使用顯式鎖(ReentrantLock)的實現
4. 典型應用場景分析
5. 性能優化與注意事項
## 一、多資源保護的核心挑戰
### 1.1 競態條件的產生
當多個線程同時訪問和修改相關聯的資源時,如果沒有適當的同步措施,就會導致數據不一致。例如銀行轉賬操作需要同時修改轉出賬戶和轉入賬戶的余額:
```java
class Account {
private int balance;
public void transfer(Account target, int amount) {
this.balance -= amount;
target.balance += amount;
}
}
為每個資源單獨加鎖可能導致死鎖:
// 錯誤示范 - 可能導致死鎖
synchronized(this) {
synchronized(target) {
this.balance -= amount;
target.balance += amount;
}
}
創建專門的鎖對象來保護所有相關資源:
class Account {
private static final Object lock = new Object();
private int balance;
public void transfer(Account target, int amount) {
synchronized(lock) {
this.balance -= amount;
target.balance += amount;
}
}
}
利用類的Class對象作為鎖:
public void transfer(Account target, int amount) {
synchronized(Account.class) {
this.balance -= amount;
target.balance += amount;
}
}
當資源數量固定且較多時,可以采用分段鎖提高并發性:
class ResourceManager {
private final Object[] locks;
private final Map<String, Resource> resources;
public ResourceManager(int segments) {
locks = new Object[segments];
for(int i=0; i<segments; i++) {
locks[i] = new Object();
}
}
public void update(String resourceId) {
int segment = resourceId.hashCode() % locks.length;
synchronized(locks[segment]) {
// 操作對應資源
}
}
}
class Account {
private static final ReentrantLock lock = new ReentrantLock();
private int balance;
public void transfer(Account target, int amount) {
lock.lock();
try {
this.balance -= amount;
target.balance += amount;
} finally {
lock.unlock();
}
}
}
避免長時間等待:
public boolean tryTransfer(Account target, int amount, long timeout)
throws InterruptedException {
if(lock.tryLock(timeout, TimeUnit.MILLISECONDS)) {
try {
this.balance -= amount;
target.balance += amount;
return true;
} finally {
lock.unlock();
}
}
return false;
}
當讀多寫少時,使用ReadWriteLock提高性能:
class ResourceCache {
private static final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private Map<String, Object> cache = new HashMap<>();
public Object get(String key) {
rwLock.readLock().lock();
try {
return cache.get(key);
} finally {
rwLock.readLock().unlock();
}
}
public void put(String key, Object value) {
rwLock.writeLock().lock();
try {
cache.put(key, value);
} finally {
rwLock.writeLock().unlock();
}
}
}
class ConnectionPool {
private final Lock lock = new ReentrantLock();
private final Condition available = lock.newCondition();
private Queue<Connection> pool = new LinkedList<>();
public Connection getConnection() throws InterruptedException {
lock.lock();
try {
while(pool.isEmpty()) {
available.await();
}
return pool.poll();
} finally {
lock.unlock();
}
}
public void releaseConnection(Connection conn) {
lock.lock();
try {
pool.offer(conn);
available.signal();
} finally {
lock.unlock();
}
}
}
class LRUCache<K,V> {
private final Lock lock = new ReentrantLock();
private Map<K,V> map = new LinkedHashMap<>(16, 0.75f, true);
private int maxSize;
public V get(K key) {
lock.lock();
try {
return map.get(key);
} finally {
lock.unlock();
}
}
public void put(K key, V value) {
lock.lock();
try {
map.put(key, value);
if(map.size() > maxSize) {
// 移除最舊條目
}
} finally {
lock.unlock();
}
}
}
盡量減少臨界區代碼量:
// 不推薦 - 鎖范圍過大
synchronized(lock) {
result = compute(); // 耗時計算
sharedResource.update(result);
}
// 推薦做法
Result result = compute(); // 在鎖外計算
synchronized(lock) {
sharedResource.update(result);
}
// 危險代碼 - 可能導致死鎖
synchronized(lockA) {
synchronized(lockB) {
// ...
}
}
// 公平鎖 - 減少線程饑餓但性能較低
ReentrantLock fairLock = new ReentrantLock(true);
// 非公平鎖 - 默認選項,吞吐量更高
ReentrantLock unfairLock = new ReentrantLock();
使用JMX檢查鎖狀態:
ReentrantLock lock = new ReentrantLock();
// 注冊到JMX...
System.out.println("等待線程數: " + lock.getQueueLength());
System.out.println("是否被持有: " + lock.isLocked());
使用同一把鎖保護多個資源是Java并發編程中的常見模式,關鍵要點包括:
通過合理設計,可以在保證線程安全的同時獲得良好的系統性能。在實際開發中,建議結合線程轉儲和性能分析工具不斷優化鎖的使用策略。
注意:本文示例代碼為演示核心概念做了簡化,實際應用中需要考慮更多邊界條件和異常處理。 “`
這篇文章共計約2300字,采用Markdown格式編寫,包含: - 多級標題結構 - 代碼塊示例 - 重點概念強調 - 實際應用場景 - 性能優化建議
可根據需要進一步擴展具體案例或添加更多性能對比數據。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。