0

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

Я предполагаю, что использование 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.

+0

Прочтите первое предложение [Javadoc] (http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CopyOnWriteArrayList.html) - это не волшебная замена , есть улов. Что еще более важно, почему вы не пишете на свой 'ConcurrentHashMap' напрямую? То, как вы написали свой код, фактически удаляет преимущества использования 'putIfAbsent', который делает именно то, что ваш' if', но поточно-безопасным способом. –

+0

Мне нужно проверить, уже ли аррайалист уже существует для этого идентификатора. если он существует, я должен добавить к нему свою ценность. –

+1

Итак, вам нужен мультимап? Хорошо, тогда я попытаюсь написать ответ. –

ответ

1

Необходимо сначала получить соответствующий список, а затем заполнить его. Что-то вроде:

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"); 

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

+0

спасибо за предложение assyilas. он отлично работал. –

+0

привет, приветствуется, должен ли быть map.replace (id, copyArr) в конце –

+0

@ user2596957 Почему? 'copyArr' - это ссылка, указывающая на список, который находится на карте, поэтому любые операции, которые вы применяете для' copyArr', выполняются в списке на карте. – assylias

1

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

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

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

Кроме того, нет абсолютно никакой необходимости в этом:

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

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


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

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

У вас есть по крайней мере два способа здесь:

  • поведения put операции в поточно-образом, т.е.
    • использование только putIfAbsent.
    • не добавляйте никаких значений в локальную копию списка, только тот, который вы берете с get.
  • Если вам нужна абсолютная, а не возможная согласованность, не используйте вообще CopyOnWriteArrayList. Вместо этого используйте обычный список с «ручной» синхронизацией. Вы можете использовать, например. Многомапсы Guava, такие как as this one, с оберткой для синхронизации, чтобы избавить вас от неприятностей (объясняет Javadoc).
+0

Хотя ваши комментарии имеют смысл, нет необходимости в дополнительной синхронизации, чтобы заставить ее работать. Это только вопрос использования значения, возвращаемого putIfAbsent. – assylias

+1

@assylias: существует, если OP нуждается в абсолютной консистенции и отказывается использовать 'CopyOnWriteArrayList'. Но я предполагаю, что вы имеете в виду первую точку, в которой слово «синхронизировать» потенциально неоднозначно. Благодарю. Я отредактирую формулировку, чтобы избежать путаницы. –

+0

Спасибо TheTerribleSwiftTomato за то, что вы указали ошибку, не используя использование возвращаемого объекта из putIfAbsent. Проблема решена. –

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