Ответ 1
зачем нам нужно синхронизировать себя с этим
synchronizemap
?
Вам может потребоваться синхронизация в уже синхронизированной коллекции, потому что вы выполняете две операции над коллекцией - в вашем примере - containsKey()
, а затем put()
. Вы пытаетесь защитить от условий гонки в коде, вызывающем коллекцию. Кроме того, в этом случае блок synchronized
также защищает значения ArrayList
, чтобы несколько потоков могли добавлять свои значения в эти несинхронизированные коллекции.
Если вы посмотрите на код, с которым вы связались, сначала проверьте наличие ключа, а затем поместите значение в карту, если ключ не существует. Вам нужно защитить от 2-х потоков, проверяющих наличие ключа, а затем оба они помещают в карту. Гонка - это первая, которая будет перезаписана предыдущим.
Синхронизированная коллекция защищает себя от множества потоков, искажающих саму карту. Он не защищает от условий логической гонки вокруг нескольких вызовов на карту.
synchronized (synchronizedMap) {
// test for a key in the map
if (synchronizedMap.containsKey(key)) {
synchronizedMap.get(key).add(value);
} else {
List<String> valuesList = new ArrayList<String>();
valuesList.add(value);
// store a value into the map
synchronizedMap.put(key, valuesList);
}
}
Это одна из причин, по которой интерфейс ConcurrentMap
имеет putIfAbsent(K key, V value);
. Это не требует двух операций, поэтому вам может не потребоваться синхронизация вокруг него.
Btw, я бы переписал вышеприведенный код:
synchronized (synchronizedMap) {
// test for a key in the map
List<String> valuesList = synchronizedMap.get(key);
if (valueList == null) {
valuesList = new ArrayList<String>();
// store a value into the map
synchronizedMap.put(key, valuesList);
}
valuesList.add(value);
}
Наконец, если в любом случае большая часть операций на карте должна быть в блоке synchronized
, вы также можете не платить за synchronizedMap
и просто использовать HashMap
всегда внутри блоков synchronized
.