← Назад к вопросам

Что такое ConcurrentHashMap и чем он отличается от synchronized HashMap?

1.8 Middle🔥 191 комментариев
#Коллекции#Многопоточность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

ConcurrentHashMap vs synchronized HashMap

ConcurrentHashMap и synchronized HashMap — оба предназначены для многопоточной работы, но подходят для разных сценариев. ConcurrentHashMap обеспечивает лучшую производительность благодаря более тонкой синхронизации.

synchronized HashMap

Вы можете синхронизировать обычную HashMap с помощью Collections.synchronizedMap():

Map<String, Integer> syncMap = Collections.synchronizedMap(new HashMap<>());
syncMap.put("key", 1);
Integer value = syncMap.get("key");

Проблема: При этом подходе вся карта заблокирована целиком при каждой операции. Это означает, что только один поток может выполнять операцию одновременно.

ConcurrentHashMap

ConcurrentHashMap использует более продвинутый подход с сегментацией (bucket-level locking):

Map<String, Integer> concurrentMap = new ConcurrentHashMap<>();
concurrentMap.put("key", 1);
Integer value = concurrentMap.get("key");

Ключевые различия

1. Механизм синхронизации

synchronized HashMap:

  • Блокирует всю карту для каждой операции
  • Использует один lock на весь объект
  • Низкая пропускная способность при высокой конкуренции потоков

ConcurrentHashMap:

  • Использует bucket-level locking (блокирует только нужный сегмент)
  • Разделяет карту на несколько сегментов (по умолчанию 16)
  • Несколько потоков могут работать с разными сегментами одновременно

2. Производительность

// Scenario: 10 потоков пишут в карту одновременно

// synchronized HashMap — все потоки ждут друг друга
Map<String, Integer> syncMap = Collections.synchronizedMap(new HashMap<>());
// Пропускная способность: низкая

// ConcurrentHashMap — потоки пишут параллельно в разные сегменты
Map<String, Integer> concurrentMap = new ConcurrentHashMap<>();
// Пропускная способность: высокая

3. Null значения

// synchronized HashMap позволяет null ключи и значения
Map<String, Integer> syncMap = Collections.synchronizedMap(new HashMap<>());
syncMap.put(null, 1);      // ✅ Работает
syncMap.put("key", null);  // ✅ Работает

// ConcurrentHashMap НЕ позволяет null
Map<String, Integer> concurrentMap = new ConcurrentHashMap<>();
concurrentMap.put(null, 1);      // ❌ NullPointerException
concurrentMap.put("key", null);  // ❌ NullPointerException

4. Итерация

Map<String, Integer> concurrentMap = new ConcurrentHashMap<>();

// synchronized HashMap требует явной синхронизации при итерации
Map<String, Integer> syncMap = Collections.synchronizedMap(new HashMap<>());
synchronized(syncMap) {
    for (Map.Entry<String, Integer> entry : syncMap.entrySet()) {
        // процесс
    }
}

// ConcurrentHashMap предоставляет "слабую консистентность"
// Не нужна явная синхронизация, но итератор может быть неактуален
for (Map.Entry<String, Integer> entry : concurrentMap.entrySet()) {
    // Безопасно, но может пропустить некоторые недавние изменения
}

5. Атомарные операции

ConcurrentHashMap имеет полезные методы для атомарных операций:

concurrentMap.putIfAbsent("key", 1);     // Положить, если ключа нет
concurrentMap.replace("key", 1, 2);      // Заменить, если значение совпадает
concurrentMap.computeIfPresent("key", (k, v) -> v + 1); // Трансформировать
concurrentMap.computeIfAbsent("key", k -> 0);         // Вычислить, если нет

Сравнительная таблица

Характеристикаsynchronized HashMapConcurrentHashMap
БлокировкаНа весь объектНа сегмент (bucket)
ПроизводительностьНизкая при высокой конкуренцииВысокая при высокой конкуренции
Null значения✅ Разрешены❌ Не разрешены
ИтерацияТребует явной синхронизацииСлабо консистентна, безопасна
Атомарные операцииНет встроенных методовputIfAbsent, replace, compute*
СложностьПростаяБолее сложная

Когда использовать что

Используй synchronized HashMap когда:

  • Нужны null ключи/значения
  • Требуется полная консистентность данных
  • Низкий уровень многопоточного доступа

Используй ConcurrentHashMap когда:

  • Высокий уровень параллельного доступа
  • Нужна высокая производительность
  • Не требуются null значения
  • Слабая консистентность приемлема

Пример производительности

import java.util.*;
import java.util.concurrent.*;

public class MapPerformanceTest {
    public static void main(String[] args) throws InterruptedException {
        // Тест synchronized HashMap
        Map<String, Integer> syncMap = Collections.synchronizedMap(new HashMap<>());
        testMap(syncMap, "synchronized HashMap");
        
        // Тест ConcurrentHashMap
        Map<String, Integer> concurrentMap = new ConcurrentHashMap<>();
        testMap(concurrentMap, "ConcurrentHashMap");
    }
    
    static void testMap(Map<String, Integer> map, String name) throws InterruptedException {
        long start = System.currentTimeMillis();
        ExecutorService executor = Executors.newFixedThreadPool(10);
        
        for (int i = 0; i < 10; i++) {
            executor.submit(() -> {
                for (int j = 0; j < 100000; j++) {
                    map.put("key" + j, j);
                }
            });
        }
        
        executor.shutdown();
        executor.awaitTermination(1, TimeUnit.MINUTES);
        
        long time = System.currentTimeMillis() - start;
        System.out.println(name + ": " + time + "ms");
    }
}

Заключение

ConcurrentHashMap — предпочтительный выбор для многопоточных приложений благодаря высокой производительности и встроенным атомарным операциям. Используй synchronized HashMap только если тебе критически нужны null значения или полная консистентность.

Что такое ConcurrentHashMap и чем он отличается от synchronized HashMap? | PrepBro