从ConcurrentMap.remove()获取密钥是否存在

时间:2012-01-04 10:26:31

标签: java concurrency thread-safety concurrenthashmap

在Java的ConcurrentMap中,有remove(key, expectedValue),这会返回以下内容之一:

  • 预期价值已经存在且已被删除。
  • 预期值不存在,因此尚未删除。

但我想得到的是:

  1. 预期价值已经存在且已被删除。
  2. 该键下有一个值,但不是预期值,因此尚未删除。
  3. 该密钥下没有任何值,因此尚未删除。
  4. 如何以并发且线程安全的方式获取此信息?


    这是我正在尝试安全的代码

    // attempt to remove the old session...
    if (!sessions.remove(player.getId(), existing)) {
        // it was not removed...
        if (sessions.containsKey(player.getId())) { // TODO threadsafe
            // ...because in the meantime some other thread logged in as that user
            throw new ServiceError(LobbyService.ERR_LOGIN_INVALID, Maps.create("reason", "already-logged-in"));
        } else {
            // ...because it was no longer there, which is as it should be
        }
    } else {
        // it was removed, which is bad, because it shouldn't have been there still
        log.warn("Kicking old session of " + player.getId() + " failed");
    }
    

    或概括:

    if (!sessions.remove(key, expected)) {
        if (sessions.containsKey(key)) {    // TODO threadsafe
            // 2
        } else {
            // 3
        }
    } else {
        // 1
    }
    

3 个答案:

答案 0 :(得分:1)

我不明白你在文档中看到的内容和你想要的内容。所以请让我写下来。

  • A与值B相关联。 remove(A, B)将返回true 删除映射A-> B(这是您想要的)。
  • A与值C相关联。 remove(A, B)将返回false,mappping A-> C不会被删除(这就是你想要的)。
  • A与没有值相关联。 remove(A, null)将返回false(这是您想要的)。

换句话说,似乎删除了你想要的东西......或者你的代码中可能还有另一个错误。

答案 1 :(得分:0)

您可以使用AtomicReference来提供帮助。假设您使用非null AtomicReference进行预先编写,则可以尝试使用谓词引用当前值为existing的会话中的值为空。如果是,则从地图中“删除”,否则AtomicReference的当前值是当前存在的值

AtomicReference<Session> ref = session.get(player.getId());
if (ref.compareAndSet(existing,null) {
   //1
}else{
   Session current = ref.get();
   if(current != null){
       //2
   }else{
      //3
   }
}

答案 2 :(得分:0)

此代码似乎几乎提供您要求的内容,但我不确定它是否能满足您的需求。

请你扩展你真正想做的事情吗?

class Player {};
ConcurrentMap<String,Player> players = new ConcurrentHashMap();

void playerIDChanged(String id, Player oldPlayer, Player newPlayer) {
  Player old = players.replace(id, newPlayer);
  if ( old == oldPlayer ) {
    // The expected value was there and has been REPLACED.
  } else {
    if ( old == null ) {
      // There is no value under that key, so it has not been removed.
    } else {
      // There is a value under that key, but not the expected one, so it HAS been replaced.
      // NB: This is slightly different from what you are asking for.
    }
  }
}