Я прочитал исходный код JDK'о ConcurrentHashMap.
Но следующий код сбил меня с толку:
public boolean isEmpty() {
final Segment<K,V>[] segments = this.segments;
...
}
Мой вопрос в следующем:
"this.segments" объявлен:
final Segment<K,V>[] segments;
Итак, здесь, в начале метода, объявлена однотипная ссылка, указывающая на одну и ту же память.
Почему автор написал это именно так? Почему они не использовали this.segments напрямую? Есть ли какая-то причина?
Это идиома, типичная для безблокировочного кода с волатильными
переменными. В первой строке вы читаете volatile
один раз и затем работаете с ней. В это время другой поток может обновить volatile
, но вас интересует только то значение, которое вы первоначально прочитали.
Кроме того, даже если переменная-член не является переменной volatile, а является конечной, эта идиома связана с кэшем процессора, так как чтение из стека более удобно для кэша, чем чтение из случайного места кучи. Также существует большая вероятность того, что локальная переменная окажется привязанной к регистру процессора.
Для последнего случая есть некоторые разногласия, поскольку JIT-компилятор обычно заботится об этих проблемах, но Дуг Ли - один из тех, кто придерживается этого принципа.
Я предполагаю, что это сделано для повышения производительности, чтобы нам нужно было получить значение поля только один раз.
Вы можете обратиться к идиоме singleton из книги effective java by Joshua Bloch
Его синглтон находится здесь:
private volatile FieldType field;
FieldType getField() {
FieldType result = field;
if (result == null) {
synchronized(this) {
result = field;
if (result == null)
field = result = computeFieldValue();
}
}
return result;
}
и он написал:
Этот код может показаться несколько запутанным. В частности, необходимость локальной переменной result может быть неясна. Эта переменная делает следующее. гарантировать, что поле будет прочитано только один раз в распространенном случае, когда оно > уже инициализировано. уже инициализировано. Хотя это не является строго необходимым, это может улучшить производительность и является более элегантным по стандартам, применяемым к низкоуровневому параллельного программирования. На моей машине приведенный выше метод работает примерно на 25 процентов быстрее, чем очевидная версия без локальной переменной.
Это может уменьшить размер байт-кода - обращение к локальной переменной в байт-коде короче, чем обращение к переменной экземпляра.
Накладные расходы на оптимизацию во время выполнения также могут быть уменьшены.
Но ни один из этих факторов не является существенным. Это больше относится к стилю кода. Если вы чувствуете себя комфортно с переменными экземпляра, конечно. Даг Ли, вероятно, чувствует себя более комфортно, имея дело с локальными переменными.