Как безопасно изменить значения в Java HashMaps одновременно? - PullRequest
1 голос
/ 14 апреля 2011

У меня есть блок кода Java, который выглядит примерно так, что я пытаюсь распараллелить:

value = map.get(key);
if (value == null) {
    value = new Value();
    map.put(key,value);
}
value.update();

Я хочу заблокировать доступ любого другого потока к карте с этим конкретным ключом до тех пор, пока value.update() не будет вызвано , даже если ключ не указан в наборе ключей . Доступ с другими ключами должен быть разрешен. Как мне этого добиться?

Ответы [ 7 ]

5 голосов
/ 14 апреля 2011

Короткий ответ: нет безопасного способа сделать это без синхронизации всего блока.Вы можете использовать java.util.concurrent.ConcurrentHashMap , однако, для получения более подробной информации см. в этой статье. Основная идея заключается в использовании ConcurrentHashMap.putIfAbsent вместо обычного put.

2 голосов
/ 14 апреля 2011

Вы не можете распараллелить обновления HashMap, потому что обновление может инициировать изменение размера базового массива, включая пересчет всех ключей.

Использовать другую коллекцию, например, java.util.concurrent.ConcurrentHashMap, которая представляет собой хэш-таблицуподдержка полного параллелизма поиска и настраиваемый ожидаемый параллелизм для обновлений. "в соответствии с Javadoc.

1 голос
/ 14 апреля 2011

Вы только что описали вариант использования вычислительной карты Guava .Вы создаете его с помощью:

Map<Key, Value> map = new MapMaker().makeComputingMap(new Function<Key, Value>() {
  public Value apply(Key key) {
    return new Value().update();
  }
));

и используете его:

Value v = map.get(key);

Это гарантирует, что только один поток вызовет update(), а другие потоки будут блокироваться и ждать, пока метод не завершится.

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

1 голос
/ 14 апреля 2011

Я бы не стал использовать HashMap, если вам нужно беспокоиться о проблемах с многопоточностью. Используйте параллельный пакет Java 5 и посмотрите на ConcurrentHashMap.

0 голосов
/ 14 апреля 2011

Одной из возможностей является управление несколькими замками. Таким образом, вы можете сохранить массив блокировок, который извлекается на основе хэш-кода ключа. Это должно дать вам лучший результат, чем синхронизация всего метода. Вы можете изменить размер массива в зависимости от количества потоков, которые, по вашему мнению, будут обращаться к коду.

private static final int NUM_LOCKS = 16;
Object [] lockArray = new Object[NUM_LOCKS];
...
// Load array with Objects or Reentrant Locks

...

Object keyLock = lockArray[key.hashcode % NUM_LOCKS];
synchronize(keyLock){
  value = map.get(key);
  if (value == null) {
    value = new Value();
    map.put(key,value);
  }
  value.update();
}
0 голосов
/ 14 апреля 2011

Просмотр Параллельная хэш-карта .Он имеет отличную производительность даже для однопоточных приложений.Это позволяет одновременно модифицировать Map из различных потоков без необходимости их блокировки.

0 голосов
/ 14 апреля 2011
private void synchronized functionname() {
    value = map.get(key);
    if (value == null) {
        value = new Value();
        map.put(key,value);
    }
    value.update();
}

Подробнее о синхронизированных методах можно узнать здесь: Синхронизированные методы

Возможно, вы захотите изучить класс ConcurrentHashMap, который подходит вашим целям. Вы можете увидеть это на JavaDoc.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...