2015-01-19 4 views
3

Я читал все и наоборот о использовании Java ConcurrentHashMap. Надеюсь, мой вопрос поможет прояснить то, что выглядит просто (с актуальными ответами).Как выполнять операции сопоставления сопоставленных объектов?

У меня есть карта, как это:

ConcurrentHashMap<Integer, ClassA> map = new ConcurrentHashMap<Integer, ClassA>() 

Я использую ConcurrentHashMap, чтобы поместить и получить операции поточно. Мой ClassA имеет некоторые атрибуты Integer/String и Collection of Strings.

Теперь я хотел бы знать, как безопасно обновлять отображаемый объект. Если я хочу, чтобы создать метод для обновления объекта из моей карты путем добавления новой строки, у меня есть что-то вроде:

 synchronized(map) 
     { 
      Collection<String> strings = map.get(id).getStrings(); 
      if(!strings.contains(newString)) //strings can't be null 
      { 
       strings.add(newString); 
      } 
     } 

Является ли этот код безопасным против одновременного чтения/записи? Можно ли это сделать по-другому, используя Java API?

+0

Возможно, у вашего примера есть некоторые ошибки. Прямо сейчас код внутри синхронизированного блока не имеет ничего общего с картой. Существует переменная с именами событий, но я не знаю, что это такое - я думаю, это ваша карта. Если это так, то вам действительно не нужно синхронизировать карту. Извлеките эту логику, вставьте ее в ClassA, а затем выполните некоторую синхронизацию. – bbankowski

+0

Действительно, я обновил описание с правильным именем. Это действительно моя карта. Я надеялся синхронизировать карту, чтобы фактически заблокировать всю таблицу и предотвратить любые одновременные изменения моих сопоставленных объектов. Можете ли вы уточнить «некоторую синхронизацию»? – TBag

+0

Простейшим способом было бы создать обычный метод void synchronized add (String newString) в вашем классе. Одновременные изменения экземпляров ClassA будут синхронизированы, и вам не придется синхронизировать их на карте. – bbankowski

ответ

0

Это попытка ответить на мой вопрос. Это сильно вдохновило мой другой вопрос ответ я нашел: What is the preferred way to modify a value in ConcurrentHashMap?

Если я мой ClassA неизменны, и заменить мой код для обновления моей карты объектов таким образом:

 ClassA oldObject = map.get(id); 
     ClassA newObject = new ClassA(oldObject, newValue);//this constructor copies the old one and add the newValue in my Collection of Strings 
     map.put(id, newEvent); 

Будет ли поточно? Я действительно хочу, чтобы мой код был чистым и эффективным, используя ConcurrentHashMap, и я считаю, что это решение идет в том же направлении. Я просто немного подозрительно отношусь к любому потоку, получая тот же объект, что и другой, между линией 2 и 3.

2

Что вы имеете в своем ответе не полностью потокобезопасны. Он заменяет значение ключа id независимо от того, что было для старого значения. Если это подходит для вашей реализации, тогда отлично, но replace(K key, V oldObj, V newObj) - это идеальный способ проверки и установки (CAS) для замены существующего значения в ConcurrentMap.

В вашем конкретном случае использования, вы должны сделать следующее

ClassA oldObj = map.get(id); 
ClassA newObject = new ClassA(oldObject, newValue); 
return map.replace(id, oldObj, newObject); 

Это гарантирует, что карта обновляется только если предыдущее значение было oldObj. Что произойдет, если этот кодовый блок вызывается разными потоками с разными newValue ?. Вышеприведенный код разрешил бы только один поток, а другой поток возвращал false.

+0

И если я хочу предотвратить любую нить между get и replace, я должен синхронизироваться на самой карте? – TBag

+0

Вам не нужно. Не более 1 вызова для замены будет успешным, если есть гонка. Во второй раз заменитель вернет false. Если вы хотите добиться успеха, вам придется поместить его в цикл do/while –

Смежные вопросы