параллельный hashmap и copyonwritearraylist

Я пытаюсь заполнить кеш, который содержит ключ/значение, с помощью ConcurrentHashMap.

Я предполагаю, что использование CopyOnWriteArrayList обеспечивает параллелизм, и у меня есть это значение для моего ключа, но я что-то упускаю в приведенном ниже коде, и он переопределяет свои значения при выполнении нескольких потоков.

if (testMap.get(id) == null) {
   CopyOnWriteArrayList<String> copyArr = new CopyOnWriteArrayList<String>();
   copyArr.add("Add Value");
   testMap().putIfAbsent(id, copyArr);
} else {                    
   testMap.put(id,testMap.get().add("Append Value"));
}

Как защитить код, который создает CopyOnWriteArrayList из нескольких потоков.

Вот исправленная версия кода в соответствии с приведенными ниже предложениями.

CopyOnWriteArrayList<Subscriber> subscriberArr =  CacheUtils.getSubscriberMap().get(syncDet.getCardNumber());

if (subscriberArr == null) {

subscriberArr = new CopyOnWriteArrayList<Subscriber>();
CopyOnWriteArrayList<Subscriber> refArr = 

cacheUtils.getSubscriberMap().putIfAbsent(syncDet.getCardNumber(), subscriberArr);

if (refArr != null) {
 subscriberArr = refArr;
}

}
 subscriberArr.add(syncDet.getSubScriber());

При повторении карты подписчиков я не вижу объект значения. размер 0 .


person Premkumar Manivannan    schedule 04.09.2013    source источник
comment
Прочтите первое предложение Javadoc - это не волшебная замена, есть одна загвоздка. Что еще более важно, почему вы не пишете своему ConcurrentHashMap напрямую? То, как вы написали свой код, на самом деле устраняет преимущество использования putIfAbsent, которое делает то же самое, что и ваш if, но безопасным для потоков способом.   -  person mikołak    schedule 04.09.2013
comment
Мне нужно проверить, существует ли уже массив для этого идентификатора. если он существует, я должен добавить к нему свое значение.   -  person Premkumar Manivannan    schedule 04.09.2013
comment
Значит, вам нужна мультимап? Хорошо, тогда я постараюсь написать ответ.   -  person mikołak    schedule 04.09.2013


Ответы (2)


Вам нужно сначала получить соответствующий список, а затем заполнить его. Что-то типа:

List<String> copyArr = testMap.get(id);
if (copyArr == null) {
    copyArr = new CopyOnWriteArrayList<String>();
    List<String> inMap = testMap.putIfAbsent(id, copyArr);
    if (inMap != null) copyArr = inMap; // already in map
}
copyArr.add("Add Value");

Таким образом, вы помещаете новый список на карту только в том случае, если его еще не было, и добавляете свой элемент в любой список, который попал на карту.

person assylias    schedule 04.09.2013
comment
привет assyilas, должен быть map.replace(id,copyArr) в конце - person Premkumar Manivannan; 05.09.2013
comment
@ user2596957 Почему? copyArr — это ссылка, указывающая на список на карте, поэтому любые операции, которые вы применяете к copyArr, выполняются в списке на карте. - person assylias; 05.09.2013
comment
Я взял приведенный выше код и реализовал свою версию для разработчиков, как показано ниже. - person Premkumar Manivannan; 05.09.2013
comment
CopyOnWriteArrayList‹Subscriber› subscriberArr = CacheUtils .getSubscriberMap().get(syncDet.getCardNumber()); if (subscriberArr == null) { subscriberArr = new CopyOnWriteArrayList‹Subscriber›(); CopyOnWriteArrayList‹Subscriber› refArr = CacheUtils .getSubscriberMap().putIfAbsent( syncDet.getCardNumber(), subscriberArr); if (refArr != null) { subscriberArr = refArr; } } subscriberArr.add(syncDet.getSubScriber()); - person Premkumar Manivannan; 05.09.2013
comment
Нет. Обновил тот же код выше. вы видите какие-либо проблемы с этим кодом? - person Premkumar Manivannan; 05.09.2013
comment
@ user2596957 Что не работает? Вы уверены, что syncDet.getCardNumber() не изменится во время выполнения? Вы должны попытаться создать простой пример, который воспроизводит проблему (т.е. я могу скопировать его и увидеть, что он не работает) и опубликовать его в новом вопросе. - person assylias; 05.09.2013

Есть несколько проблем с реализацией, которую вы сделали.

Во-первых, вы проверяете, нет ли списка для данного ключа, не поточно-ориентированным способом. Вполне может случиться так, что два потока могут выполнить if (testMap.get(id) == null) прежде, чем любой из них поместит ключ. Это не приведет к переопределению ключа как такового.

Однако создаются оба списка, и, поскольку вы добавляете элементы в эти списки в небезопасном блоке if до установки ключа, какие элементы попадают в фактическое сопоставление ключей и значений можно только догадываться.

Кроме того, в этом совершенно нет необходимости:

testMap.put(id,testMap.get(id).add("Append Value"));

В этом случае экземпляр списка уже находится на карте, вам просто нужно get его и добавить значение. Обратите внимание, что это также может испортить ваше предыдущее назначение клавиш!


Вторая потенциальная проблема заключается в том, что вы используете CopyOnWriteList, который создает новый резервный массив при добавлении новых элементов. Здесь два следствия:

  • это дорого, если много дополнений.
  • так как операция add синхронизирована (через ReentrantLock), а get нет, вы можете получить различное содержимое списка в разных потоках на короткое время (список < strong>в конечном счете подходит для добавления, однако). Это на самом деле по дизайну - CopyOnWriteArrayList ориентирован на операции hi-read/lo-write.

У вас есть как минимум два выхода:

  • conduct put operations in a thread-safe manner, i.e.
    • use only putIfAbsent.
    • не добавляйте никаких значений в локальную копию списка, только то, что вы берете из get.
  • если вам нужна абсолютная согласованность вместо конечной, вообще не используйте CopyOnWriteArrayList. Вместо этого используйте обычный список с «ручной» синхронизацией. Вы можете использовать, например. Multimap в Guava, например, как этот с оболочкой синхронизации, чтобы избавить вас от проблем (Javadoc объясняет, как это сделать).
person mikołak    schedule 04.09.2013
comment
Хотя ваши комментарии имеют смысл, нет необходимости в дополнительной синхронизации, чтобы все заработало. Это всего лишь вопрос использования значения, возвращаемого putIfAbsent. - person assylias; 04.09.2013
comment
@assylias: есть, если ОП нуждается в абсолютной согласованности и отказывается от использования CopyOnWriteArrayList. Но я предполагаю, что вы имеете в виду первый пункт, в котором слово синхронизировать потенциально двусмысленно. Я отредактирую формулировку, чтобы избежать путаницы, спасибо. - person mikołak; 04.09.2013
comment
Спасибо TheTerribleSwiftTomato за то, что он указал на ошибку, связанную с неиспользованием возвращаемого объекта из putIfAbsent. Проблема решена. - person Premkumar Manivannan; 04.09.2013
comment
@ user2596957: пожалуйста. Пожалуйста, примите один из ответов (который вам больше всего помог), чтобы этот вопрос был помечен как решенный. - person mikołak; 04.09.2013