← Назад к вопросам
Является ли HashMap потокобезопасной коллекцией?
2.3 Middle🔥 251 комментариев
#Коллекции#Многопоточность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Ответ: Является ли HashMap потокобезопасной?
Нет, HashMap НЕ является потокобезопасной коллекцией. При одновременном доступе из нескольких потоков может возникнуть состояние гонки (race condition), приводящее к потере данных, бесконечным циклам или некорректной работе.
Почему HashMap не потокобезопасна?
public class HashMap<K,V> {
transient Node<K,V>[] table; // Не synchronized
transient int size;
transient int modCount; // Счётчик модификаций
public V put(K key, V value) {
// Множество операций без синхронизации
int hash = hash(key);
int i = (n - 1) & hash;
// Операция не атомарна - может быть прервана!
}
}
Проблема: Race Condition
Map<String, Integer> map = new HashMap<>();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1_000_000; i++) {
map.put("key" + i, i);
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1_000_000; i++) {
map.put("key" + i, i * 2);
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Размер: " + map.size()); // Может быть < 2000000!
Результат: размер может быть меньше ожидаемого из-за потери данных.
Потокобезопасные альтернативы
1. Collections.synchronizedMap()
Map<String, Integer> syncMap = Collections.synchronizedMap(
new HashMap<>()
);
// Все операции синхронизированы
syncMap.put("key", 1);
Integer value = syncMap.get("key");
Минус: старая реализация, синхронизирует весь объект целиком.
2. ConcurrentHashMap (ЛУЧШЕ!)
Map<String, Integer> concurrentMap = new ConcurrentHashMap<>();
// Использует bucket-level locking
concurrentMap.put("key1", 1);
concurrentMap.put("key2", 2);
// Несколько потоков могут одновременно писать в разные buckets
Преимущества ConcurrentHashMap:
- Segment locking: вместо блокировки всей таблицы блокирует только сегмент
- Лучше масштабируется для параллельного доступа
- Итератор не throw ConcurrentModificationException (слабо согласован)
3. Hashtable (устаревший подход)
Map<String, Integer> hashtable = new Hashtable<>();
// Старая синхронизация - синхронизирует каждый метод
public synchronized V put(K key, V value) {
// ...
}
// Плохо для параллелизма - весь объект заблокирован
Сравнение производительности
Map<String, Integer> hashMap = new HashMap<>();
Map<String, Integer> concurrentMap = new ConcurrentHashMap<>();
Map<String, Integer> syncMap = Collections.synchronizedMap(new HashMap<>());
// Тест с 4 потоками, каждый добавляет 1млн элементов
ExecutorService executor = Executors.newFixedThreadPool(4);
// HashMap без синхронизации - БЫСТРО но ОПАСНО
// ConcurrentHashMap - БЫСТРО и БЕЗОПАСНО
// synchronizedMap - медленнее из-за глобальной блокировки
Когда использовать что?
// Однопоточное приложение
Map<String, Value> map = new HashMap<>(); // Выбирай это
// Многопоточное приложение
Map<String, Value> map = new ConcurrentHashMap<>(); // Выбирай это!
// Нужна итерация со снимком (редко)
map = Collections.synchronizedMap(new HashMap<>());
// Старый код (не используй в новых проектах)
Hashtable<String, Value> table = new Hashtable<>(); // Забудь об этом
Практический пример
public class UserCache {
// ПРАВИЛЬНО: потокобезопасная кеш
private final Map<String, User> cache = new ConcurrentHashMap<>();
public void putUser(String id, User user) {
cache.put(id, user);
}
public User getUser(String id) {
return cache.get(id);
}
// Можно безопасно читать из разных потоков одновременно
}
Вывод: HashMap - это коллекция для однопоточного использования. Для многопоточных приложений используй ConcurrentHashMap, которая обеспечивает потокобезопасность с лучшей производительностью благодаря сегментной блокировке.