Почему ConcurrentHashMap не всегда решает проблему многопоточности
На собеседованиях часто мы говорим что ConcurrentHashMap решает нам проблему многопоточности, так как HashMap потоконебезопасна.
Да, допустим, у нас:
Map<String, List<Order>> orders = new ConcurrentHashMap<>();
Кажется, что теперь всё потокобезопасно, но ConcurrentHashMap защищает операции с Map, а не объекты, которые лежат внутри неё.
Например:
orders.get(userId).add(order);
Если внутри обычный ArrayList, несколько потоков всё ещё могут одновременно менять этот список.
Получаем:
ConcurrentHashMap
↓
ArrayList
↑
проблема здесь
И возникает интересный архитектурный вопрос: если у нас 10 000 чтений и всего несколько обновлений, обязательно ли нам вообще нужна конкурентная модификация?
Возможно, лучше использовать immutable snapshot:
snapshot v1
↓
много читателей
обновление
↓
создали snapshot v2
↓
опубликовали
Читатели работают без блокировок со своей версией данных.
Главная мысль:
Потокобезопасная коллекция не делает потокобезопасной всю структуру данных.
Иногда лучший способ решить проблему многопоточности — убрать конкурентную модификацию из архитектуры вообще.