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

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

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

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

// 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() будет вызываться только один раз?

Ответы [ 3 ]

4 голосов
/ 19 сентября 2019

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

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

1 голос
/ 19 сентября 2019

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

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

С этим изменением можно заставить работать идиому двойной блокировки, объявив вспомогательное поле как изменчивое.Это не работает в JDK4 и более ранних версиях.

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

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

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

1 голос
/ 19 сентября 2019

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

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

...