在管理數個並行執行緒共享的雜湊表(hash table)時,確保操作的原子性非常重要。我們可以使用互斥鎖來達到這個目的。以下是兩種常見的鎖策略:保護整個表的單一鎖和為每個桶(bucket)分別設置鎖。這兩種方法各有優點和缺點。
使用單一鎖保護整個表
優點:
簡單實現:只需要一個互斥鎖,程式碼簡單易懂,易於維護。
強一致性:在操作雜湊表的任何時候,只有一個執行緒能夠訪問和修改表,確保了操作的一致性。
缺點:
低並行性:由於同一時間只有一個執行緒可以訪問整個表,這會導致高併發情況下的效能瓶頸。
高等待時間:在高併發環境下,執行緒需要等待較長時間才能獲得鎖,可能會影響整體效能。
使用每個桶的單獨鎖
優點:
高並行性:不同的桶可以同時被多個執行緒訪問和修改,從而提高了並行性和效能。
減少等待時間:鎖的競爭減少,執行緒在進行操作時等待的時間更短。
缺點:
實現較為複雜:需要為每個桶管理單獨的鎖,程式碼變得更為複雜,增加了開發和維護的難度。
死鎖風險:如果不小心設計,可能會引入死鎖問題(例如多個執行緒同時獲取多個鎖時,可能會互相等待對方釋放鎖)。
不一致性風險:需要小心處理跨桶操作,以避免不一致的狀況。
實現示例
以下是兩種方法的簡單示例。
單一鎖保護整個表
java
複製程式碼
import java.util.concurrent.locks.ReentrantLock;
import java.util.HashMap;
import java.util.Map;
public class SingleLockHashTable {
private final ReentrantLock lock = new ReentrantLock();
private final Map<Integer, String> hashTable = new HashMap<>();
public void put(int key, String value) {
lock.lock();
try {
hashTable.put(key, value);
} finally {
lock.unlock();
}
}
public String get(int key) {
lock.lock();
try {
return hashTable.get(key);
} finally {
lock.unlock();
}
}
}
每個桶的單獨鎖
java
複製程式碼
import java.util.concurrent.locks.ReentrantLock;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class BucketLockHashTable {
private static class Bucket {
private final ReentrantLock lock = new ReentrantLock();
private final List<Entry> entries = new LinkedList<>();
}
private static class Entry {
final int key;
String value;
Entry(int key, String value) {
this.key = key;
this.value = value;
}
}
private final Bucket[] buckets;
private static final int NUM_BUCKETS = 16;
public BucketLockHashTable() {
buckets = new Bucket[NUM_BUCKETS];
for (int i = 0; i < NUM_BUCKETS; i++) {
buckets[i] = new Bucket();
}
}
private int hash(int key) {
return key % NUM_BUCKETS;
}
public void put(int key, String value) {
int bucketIndex = hash(key);
Bucket bucket = buckets[bucketIndex];
bucket.lock.lock();
try {
for (Entry entry : bucket.entries) {
if (entry.key == key) {
entry.value = value;
return;
}
}
bucket.entries.add(new Entry(key, value));
} finally {
bucket.lock.unlock();
}
}
public String get(int key) {
int bucketIndex = hash(key);
Bucket bucket = buckets[bucketIndex];
bucket.lock.lock();
try {
for (Entry entry : bucket.entries) {
if (entry.key == key) {
return entry.value;
}
}
return null;
} finally {
bucket.lock.unlock();
}
}
}
總結
單一鎖保護整個表:適合簡單情況,實現簡單但並行性差。
每個桶的單獨鎖:適合高並發環境,並行性好但實現複雜且需要注意死鎖風險。
根據應用的具體需求和環境,選擇合適的鎖策略來管理並行訪問的雜湊表,可以在性能和安全性之間取得平衡。