Потокобезопасная итерация по набору ключей синхронизированной карты - PullRequest
0 голосов
/ 16 февраля 2019

Где-то в моем многопоточном коде у меня есть карта (доступ к которой осуществляется несколькими потоками), объявленная так:

private Map<Foo, Integer> fooMap =
    Collections.synchronizedMap(new HashMap<Foo, Integer>());

Этот же класс предоставляет открытый метод

public Collection<Foo> getFooList() {
    return fooMap.keySet();
}

В другом месте моего кода я перебираю коллекцию, возвращаемую getFooList().Мне известно, что большинство операций на синхронизированной карте являются поточно-ориентированными, за исключением одного итерации, которая должна быть явно синхронизирована.Я реализовал это следующим образом:

synchronized(bar.getFooList()) {
    for (Foo foo : bar.getFooList()) {
        // do stuff with foo
    }
}

Время от времени я получаю ConcurrentModificationException для оператора for.Мне интересно, синхронизируюсь ли я с неправильным экземпляром класса. Должен ли я синхронизироваться с картой, а не с ее набором ключей?Опять же, я действительно не хочу открывать всю карту другим классам (она является закрытой по какой-то причине).

Как перебрать набор ключей потокобезопасным способом, без необходимости выставлятьвся карта?

1 Ответ

0 голосов
/ 16 февраля 2019

С Javadoc :

Обязательно, чтобы пользователь вручную синхронизировался на возвращенной карте при переборе по любому из представлений своей коллекции:

  Map m = Collections.synchronizedMap(new HashMap());
      ...
  Set s = m.keySet();  // Needn't be in synchronized block
      ...
  synchronized (m) {  // Synchronizing on m, not s!
      Iterator i = s.iterator(); // Must be in synchronized block
      while (i.hasNext())
          foo(i.next());
  }

Несоблюдение этого совета может привести к недетерминированному поведению.Возвращенная карта будет сериализуемой, если указанная карта является сериализуемой.

Вы можете использовать ConcurrentHashMap, что гарантирует параллелизм на keySet.См. здесь :

Итератор представления является «слабосогласованным» итератором, который никогда не вызовет исключение ConcurrentModificationException и гарантирует прохождение элементов в том виде, в каком они существовали при создании итератора, и может(но не гарантируется) отражать любые модификации, следующие за конструкцией.

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