предисловие
ConcurrentHashMap широк и глубок, как видно из его более чем 50 внутренних классов, кажется, что суть параллелизма JDK в нем. Но у него все еще есть хороший API для нас, и программист вообще не чувствует сложности внутри него. Однако каждый метод внутри него чрезвычайно сложен, даже метод размера довольно сложен.
Давайте посмотрим на этот метод размера сегодня.
метод размера
код показывает, как показано ниже:
public int size() {
long n = sumCount();
return ((n < 0L) ? 0 : (n > (long)Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int)n);
}
Maximum возвращает максимальное значение int, но длина этой карты может превышать максимальное значение int, поэтому JDK 8 добавляет метод mappingCount. код показывает, как показано ниже:
public long mappingCount() {
long n = sumCount();
return (n < 0L) ? 0L : n; // ignore transient negative values
}
По сравнению с методом size возвращаемое значение метода mappingCount имеет тип long. Таким образом, нет необходимости ограничивать максимальное значение значением Integer.MAX_VALUE. JDK рекомендует использовать этот метод. Но это возвращаемое значение все же не обязательно абсолютно точное.
Как видно из этих двух методов, метод sumCount является основным.
Реализация метода SumCount
код показывает, как показано ниже:
final long sumCount() {
CounterCell[] as = counterCells; CounterCell a;
long sum = baseCount;
if (as != null) {
for (int i = 0; i < as.length; ++i) {
if ((a = as[i]) != null)
sum += a.value;
}
}
return sum;
}
Вышеприведенная логика метода: когда counterCells не равен нулю, он проходит по элементам и накапливается с помощью baseCount.
Два свойства: baseCount и counterCells.
Сначала посмотрите на Basecount.
/**
* Base counter value, used mainly when there is no contention,
* but also as a fallback during table initialization
* races. Updated via CAS.
* 当没有争用时,使用这个变量计数。
*/
private transient volatile long baseCount;
Переменная Volatile использует ее в методе AddCount, а метод AddCount вызывается после PUT. Эта переменная создается в методе AddCount.
Но что, если параллелизм приведет к сбою CAS? Используйте счетчик ячеек.
Если приведенный выше CAS дает сбой, в методе fullAddCount операция бесконечного цикла будет продолжаться до тех пор, пока она не завершится успешно.
И этот класс CounterCell является призраком наверху?
// 一种用于分配计数的填充单元。改编自LongAdder和Striped64。请查看他们的内部文档进行解释。
@sun.misc.Contended
static final class CounterCell {
volatile long value;
CounterCell(long x) { value = x; }
}
Класс, отмеченный @sun.misc.Contended, имеет внутри volatile переменную. В примечании говорится, адаптировано из LongAdder и Striped64, для этих двух классов см.Java8 Striped64 и LongAdder.
А по поводу этой аннотации необходимо пояснить. Эта аннотация идентифицирует класс, чтобы предотвратить необходимость предотвращения «ложного совместного использования».
Разговор о псевдосовместном использовании. Чтобы процитировать кого-то еще:
Избегайте ложного обмена. Сначала процитируйте объяснение псевдосовместного использования: Кэш-система хранится в единицах строк кеша. Строка кэша представляет собой целую степень двух последовательных байтов, Обычно 32-256 байт. Наиболее распространенный размер строки кэша составляет 64 байта. Когда несколько потоков изменяют взаимно независимые переменные, Если эти переменные используют одну и ту же строку кэша, они могут непреднамеренно повлиять на производительность друг друга, что является ложным разделением.
Таким образом, ложное совместное использование чрезвычайно вредно для производительности.
До версии JDK 8 такой аннотации не было, для решения этой проблемы Дуг Ли использовал сплайсинг, заполняя строку кэша, чтобы модификации между кэшами не влияли друг на друга.
Проверено на моей машине, разрыв в производительности с этой аннотацией и без нее составляет 5 раз.
Суммировать
Итак, вот краткое введение в метод Size. в заключении:
JDK 8 рекомендует использовать метод mappingCount, так как возвращаемое значение этого метода имеет тип long и не будет ограничивать максимальное значение, поскольку метод size имеет тип int (метод size определяется интерфейсом и не может быть изменен).
При отсутствии параллелизма достаточно использовать volatile переменную baseCount, при параллельном, после того как CAS не сможет модифицировать baseCount, будет использован класс CounterCell, и будет создан объект, обычно свойство volatile value объекта равно 1 . При вычислении размера значения элементов в массивах baseCount и CounterCell суммируются для получения общего размера, но это число все равно может быть неточным.
Еще один момент, который следует отметить, заключается в том, что этот класс CounterCell помечен аннотацией @sun.misc.Contended, которая предотвращает ложное совместное использование. Он был добавлен в 1.8. При использовании необходимо добавить-XX:-RestrictContended
параметр.