Получить ли ключ существовал из ConcurrentMap.remove () - PullRequest
2 голосов
/ 04 января 2012

В Java ConcurrentMap есть remove(key, expectedValue), это возвращает одно из:

  • Ожидаемое значение было там и было удалено.
  • Ожидаемого значения не было, поэтому оно не было удалено.

Но я хочу получить одно из:

  1. Ожидаемое значение было там и было удалено.
  2. Под этим ключом есть значение, но не ожидаемое, поэтому оно не было удалено.
  3. Под этим ключом нет значения, поэтому он не был удален.

Как я могу получить эту информацию одновременно и потокобезопасным способом?


Это код, который я пытаюсь обезопасить

// 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 ]

1 голос
/ 04 января 2012

Я не понимаю, что вы видели в документе и что вы хотите.Поэтому, пожалуйста, позвольте мне записать вещи.

  • ключ A связан со значением B.remove(A, B) вернет true и удалить отображение A-> B (что вам нужно).
  • ключ A связан со значением C.remove(A, B) вернет false, отображение A-> C не будет удалено (что вам нужно).
  • ключ A не связан со значением.remove(A, null) вернет false (это то, что вы хотите).

Другими словами, кажется, что удаление точно делает то, что вы хотите ... или, возможно, есть другая ошибка в вашем коде.

0 голосов
/ 12 января 2012

Этот код, кажется, почти предлагает то, что вы просите, но я не уверен, что он даст вам то, что вы хотите.

Не могли бы вы рассказать о том, что вы на самом деле хотите сделать?

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.
    }
  }
}
0 голосов
/ 04 января 2012

Вы можете использовать AtomicReference, чтобы помочь. Предполагая, что вы выполняете предварительную подготовку с ненулевым 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
   }
}
...