エントリごとにロックを保持する場合があります。これにより、2 つのスレッドが同じ要素にアクセスしようとしない限り、ロックなしの同時更新が可能になります。
class LockedReference<T> {
Lock lock = new ReentrantLock();;
T value;
LockedReference(T value) {this.value=value;}
}
LockedReference<T> ref = new LockedReference(value);
ref.lock.lock(); //lock on the new reference, there is no contention here
try {
if (map.putIfAbsent(key, ref)==null) {
//we have locked on the key before inserting the element
doSomethingElse();
}
} finally {ref.lock.unlock();}
後で
Object value;
while (true) {
LockedReference<T> ref = map.get(key)
if (ref!=null) {
ref.lock.lock();
//there is no contention, unless a thread is already working on this entry
try {
if (map.containsKey(key)) {
value=ref.value;
break;
} else {
/*key was removed between get and lock*/
}
} finally {ref.lock.unlock();}
} else value=null;
}
より洗練されたアプローチは、書き換えであり、 (要素が配置された場合に実行される)を受け入れるConcurrentHashMap
バージョンを持っています。しかし、それははるかに複雑です。putIfAbsent
Runnable
基本的に、ConcurrentHashMap
エントリごとに 1 つのロックとマップ全体の 1 つのグローバル ロックの中間にある、ロックされたセグメントを実装します。