Почему я могу пропустить любую синхронизацию для DCL, если я использую 32-битные примитивы и идемпотентную функцию?

Я прочитал следующую классическую известную статью: Декларация о нарушении

У меня есть вопрос о:

На самом деле, если предположить, что функция calculateHashCode всегда возвращает один и тот же результат и не имеет побочных эффектов (т. е. идемпотента), вы даже можете избавиться от всей синхронизации.

// Lazy initialization 32-bit primitives
// Thread-safe if computeHashCode is idempotent
class Foo { 
  private int cachedHashCode = 0;
  public int hashCode() {
    int h = cachedHashCode;
    if (h == 0) {
      h = computeHashCode();
      cachedHashCode = h;
      }
    return h;
    }
  // other functions and members...
  }

Статья написана для java 4. Актуальна ли она для java 8+?

Правда ли, что computeHashCode() будет вызываться только один раз?


person gstackoverflow    schedule 19.09.2019    source источник
comment
Вы отметили вопрос dcl, но я не вижу никакой связи с языком цифровых команд.   -  person HABO    schedule 19.09.2019
comment
@HABO спасибо, исправлено. Я ожидал, что это блокировка с двойной проверкой   -  person gstackoverflow    schedule 20.09.2019


Ответы (3)


Нет, это может быть выполнено несколько раз. Синхронизация «сбрасывает» изменения в другие потоки, и без нее изменения, сделанные в одном потоке, теоретически никогда не могут быть переданы за пределы этого потока.

Но в статье не сказано, что computeHashCode будет вызываться ровно один раз, просто состояние гонки не вызывает никаких проблем. Записи int гарантированно будут атомарными, и если computeHashCode является идемпотентным (что требует, чтобы класс и его поля были фактически окончательными), вы просто перезапишете предыдущие значения тем же значением, так что ничего плохого не произойдет.

person gustafc    schedule 19.09.2019
comment
Проблема в том, что статья вообще ничего не объясняет - person gstackoverflow; 19.09.2019

Нет, не гарантируется, что он будет вызван только один раз, но это не имеет значения.

Если computeHashCode является идемпотентным, дополнительные вызовы не дадут эффекта, и поэтому потоки не смогут мешать друг другу.

person ThisIsNoZaku    schedule 19.09.2019

Из той же статьи, которую вы разместили:

JDK5 и более поздние версии расширяют семантику для volatile, так что система не позволяет переупорядочивать запись volatile по отношению к любому предыдущему чтению или записи, а чтение volatile нельзя переупорядочивать по отношению к любому последующему чтению или записи.

Благодаря этому изменению можно заставить работать идиому Double-Checked Locking, объявив вспомогательное поле volatile. Это не работает в JDK4 и более ранних версиях.

это означает, что блокировка с двойной проверкой безопасна, начиная с Java 5.

Правда ли, что calculateHashCode() будет вызываться только один раз?

Нет. Метод никак не синхронизируется. Однако он является потокобезопасным, поскольку является идемпотентным.

person Pavel Smirnov    schedule 19.09.2019