У меня вопрос по использованию карт в многопоточном приложении. Предположим, у нас есть такой сценарий:
- Поток получает данные json как
List<Map<String, Object>>
, которые десериализуются Джексоном Джсоном. - Этот поток изменяет полученные карты.
- А затем помещает список в очередь блокировки для использования другим потоком.
Как видите, карта модифицируется только одним потоком, но затем она становится доступной только для чтения (ничего не меняется, просто больше не модифицируется) и передается другому потоку. Затем, когда я изучил реализации HasMap
(также TreeMap
) и ConcurrentHashMap
, последний имеет volatile
полей, а первые два — нет. Итак, какую реализацию Map
мне следует использовать в этом случае? Является ли ConcurrentHashMap
излишним выбором или его должно использовать из-за передачи между потоками?
Мои простые тесты показывают, что я могу использовать HashMap/TreeMap
, когда они изменяются синхронно, и это работает, но мой вывод или мой тестовый код могут быть неверными:
def map = new TreeMap() // or HashMap
def start = new CountDownLatch(1)
def threads = (1..5)
println("Threads: " + threads)
def created = new CountDownLatch(threads.size())
def completed = new CountDownLatch(threads.size())
threads.each {i ->
new Thread({
def from = i * 10
def to = from + 10
def local = (from..to)
println(Thread.currentThread().name + " " + local)
created.countDown()
start.await()
println('Mutating by ' + local)
local.each {number ->
synchronized (map) {
map.put(number, ThreadLocalRandom.current().nextInt())
}
println(Thread.currentThread().name + ' added ' + number + ': ' + map.keySet())
}
println 'Done: ' + Thread.currentThread().name
completed.countDown()
}).start()
}
created.await()
start.countDown()
completed.await()
println('Completed:')
map.each { e ->
println('' + e.key + ': ' + e.value)
}
Основной поток порождает 5 дочерних потоков, которые синхронно обновляют общую карту, когда они завершаются, основной поток успешно видит все обновления дочерними потоками.
Collections.immutableMap(Map map)
. Таким образом, вы уверены, что параллельная модификация никогда не произойдет. - person Polygnome   schedule 18.09.2020final
и неvolatile
, это также не гарантирует вам параллелизма! Неизменяемость, как вы это делаете здесь (например, обертывание с помощьюunmodifiableMap
), оставляет исходную карту структурно неизменной и такой же безопасной или небезопасной, как и раньше. Если вам нужны конструкции, безопасные для параллелизма, этого недостаточно. Вам нужна неизменность + безопасная публикация и/или «происходит до» (java.util.concurrent). - person GPI   schedule 18.09.2020Collections.unmodifiableMap(…)
, использует полеfinal
. Если вы получаете доступ к исходной карте только через это поле (что означает, что вы никогда не будете изменять исходную карту после создания оболочки), у вас есть гарантии неизменяемости объектов. Тем не менее, отказ от безопасной публикации редко приносит пользу. - person Holger   schedule 24.09.2020