Короткий ответ
Синхронизированные коллекции — это обёртки над обычными коллекциями, создаваемые через Collections.synchronizedList(), synchronizedMap() и т. п., а также исторические Vector и Hashtable: в них каждый публичный метод синхронизирован целиком, все операции захватывают один общий монитор коллекции. Concurrent-коллекции из java.util.concurrent (ConcurrentHashMap, CopyOnWriteArrayList и др.) спроектированы для высокой конкуренции: они используют гранулярную синхронизацию, CAS и слабо консистентные итераторы.
Как это работает подробнее
Synchronized-коллекции гарантируют потокобезопасность, но создают сильную конкуренцию: чем больше потоков обращается одновременно, тем больше времени они проводят в ожидании общей блокировки, поэтому масштабируемость плохая. Итерироваться по ним нужно внутри собственного блока synchronized, иначе поведение при параллельном изменении недетерминировано.
ConcurrentHashMap в современных версиях синхронизирует доступ по корзинам таблицы: операции над разными корзинами не мешают друг другу, при вставке в пустую корзину применяется CAS без блокировки, а чтение в большинстве случаев вообще не блокируется.
- Итераторы concurrent-коллекций слабо консистентные (weakly consistent): не бросают ConcurrentModificationException и отражают состояние на момент обхода, но не гарантируют снимок.
- CopyOnWriteArrayList копирует весь массив при каждой модификации: читатели не блокируются и всегда видят консистентное состояние — это идеально при чтениях, значительно превышающих записи.
Пример кода
import java.util.*;
import java.util.concurrent.*;
public class CollectionsComparison {
public static void main(String[] args) {
// synchronized-обёртка: каждый метод под общим монитором
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
syncList.add("a");
// concurrent-коллекция: гранулярная синхронизация
Map<String, String> map = new ConcurrentHashMap<>();
map.put("key", "value");
// CopyOnWriteArrayList: копирование массива при записи
List<String> cow = new CopyOnWriteArrayList<>();
cow.add("b");
}
}