2

我试图找到一种以ConcurrentHashMap原子方式对 a 执行多个操作的方法。

我的逻辑是这样的:

if (!map.contains(key)) {
    map.put(key, value);

    doSomethingElse();
}

我知道有putIfAbsent方法。但是如果我使用它,我仍然无法doSomethingElse原子地调用它。

除了诉诸同步/客户端锁定之外,还有什么方法可以做这些事情吗?

如果它有帮助,doSomethingElse在我的情况下将非常复杂,涉及创建和启动一个线程来查找我们刚刚添加到映射中的键。

4

4 回答 4

5

如果有帮助,在我的例子中 doSomethingElse 将非常复杂,涉及创建和启动一个线程来查找我们刚刚添加到映射中的键。

如果是这种情况,您通常必须在外部进行同步。

在某些情况下(取决于doSomethingElse()预期映射的状态是什么,以及其他线程可能会执行映射什么),以下操作也可能起作用:

if (map.putIfAbsent(key, value) == null) {
    doSomethingElse();
}

这将确保对于任何给定的键只有一个线程进入doSomethingElse()

于 2013-03-19T13:41:29.090 回答
3

除非您希望所有放置线程都等到第一个成功的线程放入映射中,否则这将起作用。

if(map.get(key) == null){

  Object ret = map.putIfAbsent(key,value);
  if(ret == null){ // I won the put
     doSomethingElse();
  }
}

现在,如果许多线程都使用相同的线程,则key只有一个会获胜,并且只有一个会获胜doSomethingElse()

于 2013-03-19T13:58:49.763 回答
2

如果您的设计要求在没有其他人访问地图的情况下将地图访问和其他操作分组,那么您别无选择,只能锁定它们。也许可以重新设计设计以避免这种需要?

这也意味着对映射的所有其他访问必须在同一个锁后面进行序列化。

于 2013-03-19T13:41:30.477 回答
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接受 a的版本Runnable(如果放置了元素则执行)。但这会复杂得多。

基本上,ConcurrentHashMap实现锁定段,它位于每个条目一个锁和整个地图的一个全局锁之间。

于 2013-03-19T13:52:27.220 回答