SynchronizedMap, ConcurrentHashMap и многопоточность
В сегодняшнем посте, я хочу рассказать как работают synchronizeHashMap и ConcurrentHashMap. Как они ведут себя в многопоточный среде и в чём их главное отличие.
Начнём из далека. Что такое HashMap - это key/value хранилище с особой структурой хранения данных. Ключевое слово здесь Hash(Hashing). Hashing - это процесс вычисления hash-функции ключа записываемого значения. Именно по этому параметру, значение кладется в определённой место в hashTable называемое hash bucket.
Слева от 0 до 6, это так называемые hash buckets. Допустим, мы хотим добавить значение в нашу HashMap - put(‘PIG’,29). Первое что происходит, это вычисление hash по ключу. После это значение пропускатеся через фильтр (остаток от деления по модулю 6 в данном примере). Это необходимо для нахождения определённого hash bucket-а(3). Ключ и значение преобразуются в определённую структуру (Entry).
И добавляются в коллекцию, подобную LinkedList.
По простому HashMap - это массив hash-bucket-ов, которые хранят в себе данные в структуре связанного списка.
Synchronized Map - это потокобезопасная коллекция данных основанная на взаимоблокировках. Что это нам говорит? Все методы, связанные с добавлением/удалением данных, синхронизированы, и для получения значения из одного потока нам необходимо ждать, пока другой поток завершит работу с Map-ой. Разновидностью синхронизированной мапы является HashTable (JDK 1.0).
Для небольшого количества потоков, или где необходимо обезопасить свои данные в конкуретной среде потоков, это работает более менее приемлемо. Но когда необходимо использовать Map в хаотическом мире многопоточного программирования, на сцену выходит ConcurrentHashMap.
ConcurrentHashMap - это тот же HashMap, но рассчитан для работы в многопоточной среде. Главным отличием является то, что ему не нужно иметь синхронизированные блоки данных для доступа или записи информации. То есть ему ненужно блокировать самого себя для других потоков. Но как же тогда он корректно работает в многопоточной среде ? и какие требования выдвигались со стороны разработчиков?
- Потокобезопасность
- Отсутствие блокировок всей таблицы во время доступа к ней
- Желательно, чтобы отсутствовали блокировки таблицы при выполнении операции чтения
В ConcurrentHashMap был введен новый слой абстракции, называемый сегментами (Segment). Другими слова Segment - это массив хэш-таблиц (концептуально).
ConcurrentHashMap делиться на сегменты (по умолчанию 16) при инициализации. Именно сегменты нам позволяют использовать нашу Map-у, множеством потоков без блокирования всего объекта. Другими словами, когда один поток занят пятым сегментом, 15 сегментов свободны для взаимодействия с другими потоками. То есть, происходит блокировка только одного сегмента, а не целого объекта.
Ниже приведена таблица тестов HashTable и ConcurrentHashMap в многопоточной среде.
Threads | ConcurrentHashMap | Hashtable |
---|---|---|
1 | 1.00 | 1.03 |
2 | 2.59 | 32.40 |
4 | 5.58 | 78.23 |
8 | 13.21 | 163.48 |
16 | 27.58 | 341.21 |
32 | 57.27 | 778.41 |
В этой статье я только поверхностно коснулся ConcurrentHashMap и synchronized Map. Для более глубокого понимания прошу вас в мир документации Java - http://docs.oracle.com/javase/7/docs/api/