Какие потокобезопасные коллекции используются в Java
Что проверяют
Знаете ли вы современные конкурентные коллекции и понимаете ли, чем они лучше синхронизированных обёрток.
Как отвечать
Есть три поколения, и важно их не путать.
Устаревшее: Vector и Hashtable. Синхронизированы целиком, каждая операция берёт одну блокировку на весь объект. В новом коде не используются.
Обёртки: Collections.synchronizedList и подобные. Оборачивают обычную коллекцию одной блокировкой. Проблема та же, плюс обход всё равно требует внешней синхронизации, иначе получите ConcurrentModificationException.
Конкурентные коллекции из java.util.concurrent, это и есть правильный ответ:
- ConcurrentHashMap: чтение без блокировок, запись блокирует только нужную корзину. Есть атомарные
compute,merge,putIfAbsent, которыми делают счётчики и кэши; - CopyOnWriteArrayList: каждая запись копирует массив, зато чтение полностью без блокировок. Для списков слушателей, где читают часто, а меняют редко;
- ConcurrentLinkedQueue и ConcurrentLinkedDeque: неблокирующие очереди на compare-and-swap;
- BlockingQueue (
ArrayBlockingQueue,LinkedBlockingQueue,SynchronousQueue): очередь с ожиданием, основа пулов потоков и схемы «производитель и потребитель»; - ConcurrentSkipListMap: конкурентный отсортированный аналог
TreeMap.
Ключевая мысль: конкурентная коллекция делает атомарной отдельную операцию, но не последовательность из двух. Проверить и положить надо одним вызовом putIfAbsent или compute, иначе гонка остаётся.
Где спотыкаются
Предлагают Collections.synchronizedMap как современное решение.
Делают if (!map.containsKey(k)) map.put(k, v) на ConcurrentHashMap и получают гонку.
Берут CopyOnWriteArrayList для часто изменяемого списка и упираются в копирование на каждой записи.
Не знают, что итератор конкурентной коллекции слабо согласован: он не бросает исключение, но и не гарантирует, что увидит свежие изменения.