Структура данных Concurrenthashmap Iterator
Мне нужно было спросить о следующих аспектах ConcurrentHashMap, так как я не могу понять это из исходного кода.
(Обратите внимание, что я не спрашиваю о поведении, которое хорошо понято. Речь идет о механизме, который итератор использует для отображения поведения)
"The iterator is guaranteed to reflect the state of the map at the time of it's creation."
1. Значит ли это, что итератор получает свою собственную копию карты поддержки? Почему иначе volatile чтения не дает истинного состояния 'value', даже после создания итератора? (точное местоположение кода будет оценено)
2. Как неблокирующему чтению и итерации удается вести себя согласованно, даже когда сегмент подвергается повторному хэшированию?
1 ответ
Как уже упоминалось в комментариях, я не уверен, что ConcurrentHashMap
поведение "хорошо понято". Утверждение, сделанное в вашей неназванной цитате, неверно для ConcurrentHashMap.
Итератор для ConcurrentHashMap слабо согласован... Javadoc заявляет:
Итераторы, Spliterators и Enumerations возвращают элементы, отражающие состояние хеш-таблицы в какой-то момент или после создания итератора / перечисления.
В свете этого,
Краткий ответ на ваш вопрос 1: нет, он не получает свою собственную копию карты
Краткий ответ на ваш вопрос 2: " он не ведет себя последовательно"
Вот некоторые другие соответствующие цитаты из Javadoc s, которые помогают объяснить реализацию:
Основная стратегия заключается в разделении таблицы между сегментами, каждый из которых сам по себе является одновременно читаемой хеш-таблицей...
Сегменты поддерживают таблицу списков записей, которые всегда находятся в согласованном состоянии, поэтому их можно читать (с помощью произвольного чтения сегментов и таблиц) без блокировки. Это требует репликации узлов при необходимости во время изменения размера таблицы, поэтому старые списки могут быть просмотрены читателями, все еще использующими старую версию таблицы.
Репликация узла происходит для каждого сегмента в его методе rehash. Javadoc s для метода Segment.rehash() объясняют функциональность:
Переклассифицировать узлы в каждом списке в новую таблицу. Поскольку мы используем расширение степени двойки, элементы из каждой ячейки должны либо оставаться с тем же индексом, либо перемещаться со степенью смещения два. Мы исключаем ненужное создание узлов, отслеживая случаи, когда старые узлы могут быть повторно использованы, потому что их следующие поля не изменятся. Статистически, при пороге по умолчанию, только одна шестая из них нуждается в клонировании, когда таблица удваивается. Заменяемые ими узлы будут собираться мусором, как только на них больше не будет ссылаться ни один поток чтения, который может быть в середине одновременно проходящей таблицы. Доступ к записи использует обычную индексацию массива, потому что за ним следует изменчивая запись в таблицу.
Пожалуйста, ознакомьтесь с отличной статьей Ричарда Бернисона для хорошего краткого описания деталей реализации ConcurrentHashMap для jdk 7.
Для дополнительной информации:
Javadoc s для ConcurrentHashMap jdk8