Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Java Новый топик    Ответить
 Почему DCL без volatile работает для 32-bit примитивов ?  [new]
questioner
Member

Откуда:
Сообщений: 1861
P.S. я знаю, что не надо использовать DCL. интерес сугубо академический.

Решил изучить таки первоисточник с описанием проблемы DCL: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
Вот есть такой код:




// Broken multithreaded version
// "Double-Checked Locking" idiom
class Foo { 
  private Helper helper = null;
  public Helper getHelper() {
    if (helper == null) 
      synchronized(this) {
        if (helper == null) 
          helper = new Helper();
      }    
    return helper;
    }
  // other functions and members...
  }


Ну и как мне уже давно известно это не будет работать потому, что поток, который не входит в критическую секцию может увидеть helper уже не null, но конструктор Helper ещё не закончился. Всё вроде ясно кроме одной детали. А что если конструктор пустой? в этом случае проблема тоже будет?

Также там пишут, что
автор
Although the double-checked locking idiom cannot be used for references to objects, it can work for 32-bit primitive values (e.g., int's or float's). Note that it does not work for long's or double's, since unsynchronized reads/writes of 64-bit primitives are not guaranteed to be atomic.


И мол вот так работать будет:
// Correct Double-Checked Locking for 32-bit primitives
class Foo { 
  private int cachedHashCode = 0;
  public int hashCode() {
    int h = cachedHashCode;
    if (h == 0) 
    synchronized(this) {
      if (cachedHashCode != 0) return cachedHashCode;
      h = computeHashCode();
      cachedHashCode = h;
      }
    return h;
    }
  // other functions and members...
  }


Но что-то я ума не приложу почему. Вот допустим первый тред выполнил строку
cachedHashCode = h;


Второй тред может увидеть, что эта строка выполнилась, но при этом не увидеть предыдущие две строки. Но в этом случае я вообще хз что он может увидеть. По всей видимости тут оптимизации сработать не должны. Только я не понимаю почему.
19 сен 19, 15:42    [21974389]     Ответить | Цитировать Сообщить модератору
 Re: Почему DCL без volatile работает для 32-bit примитивов ?  [new]
Leonid Kudryavtsev
Member

Откуда:
Сообщений: 7927
questioner
Второй тред может увидеть, что эта строка выполнилась, но при этом не увидеть предыдущие две строки. Но в этом случае я вообще хз что он может увидеть. По всей видимости тут оптимизации сработать не должны. Только я не понимаю почему.


Раз не null - значит результат подсчитан
Поскольку он int, он атомарный (т.е. не может быть не до конца записанный/подсчитанный /в отличие от объекта/)

Т.ч. "не увидить предыдущие две строки" он физически не может. Что значет "не увидеть", когда результат уже есть и он корректный (т.к. атомарный)

IMHO Хороший вопрос Вы задали, эта хренотень с непольностью проинициализированными объектами мне вообще не понятна (((, в свое время дофига времени потерял на отладке проекта, где это вылезало

AFAIK Если класс объявлен как имутабельный, то вроде конструктор гарантирует атомарность инициализацию объекта и такой хренотени быть не должно, надеюсь точную ссылку на доку кто нибудь сможет дать, у меня под рукой нет
19 сен 19, 16:25    [21974444]     Ответить | Цитировать Сообщить модератору
 Re: Почему DCL без volatile работает для 32-bit примитивов ?  [new]
questioner
Member

Откуда:
Сообщений: 1861
Leonid Kudryavtsev,

спасибо за ответ


Leonid Kudryavtsev
AFAIK Если класс объявлен как имутабельный, то вроде конструктор гарантирует атомарность инициализацию объекта и такой хренотени быть не должно, надеюсь точную ссылку на доку кто нибудь сможет дать, у меня под рукой нет


Примерно по поводу этого из той же статьи:

автор
In fact, assuming that the computeHashCode function always returned the same result and had no side effects (i.e., idempotent), you could even get rid of all of the synchronization.


// 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...
  }
19 сен 19, 16:45    [21974465]     Ответить | Цитировать Сообщить модератору
 Re: Почему DCL без volatile работает для 32-bit примитивов ?  [new]
забыл ник
Member

Откуда:
Сообщений: 3024
questioner

Примерно по поводу этого из той же статьи:

автор
In fact, assuming that the computeHashCode function always returned the same result and had no side effects (i.e., idempotent), you could even get rid of all of the synchronization.


// 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...
  }


Если операция идемпотентна и атомарна как в этом случае - то зачем вообще может понадобиться синхронизация? Что-то или кто-то недоговаривает.
Для начала надо определиться что этот код вообще должен делать? Считать хэшкод один раз? Считать несколько но главное чтобы корректно? Или еще что? А что если хэшкод вернет 0, автор не подумал? Короче я не понимаю идею, что вообще хочется понять то?
19 сен 19, 16:53    [21974480]     Ответить | Цитировать Сообщить модератору
 Re: Почему DCL без volatile работает для 32-bit примитивов ?  [new]
questioner
Member

Откуда:
Сообщений: 1861
забыл ник
questioner
Примерно по поводу этого из той же статьи:

пропущено...


// 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...
  }


Если операция идемпотентна и атомарна как в этом случае - то зачем вообще может понадобиться синхронизация? Что-то или кто-то недоговаривает.
Для начала надо определиться что этот код вообще должен делать? Считать хэшкод один раз? Считать несколько но главное чтобы корректно? Или еще что? А что если хэшкод вернет 0, автор не подумал? Короче я не понимаю идею, что вообще хочется понять то?


Закешировать наверное хотят, но я лично не могу понять действительно ли это выполнится только один раз или имеется ввиду, что выполнится и выполнится, всё равно одно и то же вернёт. На ноль решили забить - Шипилёв чо то такое кстати рассказывал кстати применительно к Strring- реально не большая проблема.

Статья - классика.
автор
Signed by: David Bacon (IBM Research) Joshua Bloch (Javasoft), Jeff Bogda, Cliff Click (Hotspot JVM project), Paul Haahr, Doug Lea, Tom May, Jan-Willem Maessen, Jeremy Manson, John D. Mitchell (jGuru) Kelvin Nilsen, Bill Pugh, Emin Gun Sirer
Не самые последние люди в java сообществе. Не находите?
19 сен 19, 17:02    [21974495]     Ответить | Цитировать Сообщить модератору
 Re: Почему DCL без volatile работает для 32-bit примитивов ?  [new]
забыл ник
Member

Откуда:
Сообщений: 3024
questioner
забыл ник
пропущено...


Если операция идемпотентна и атомарна как в этом случае - то зачем вообще может понадобиться синхронизация? Что-то или кто-то недоговаривает.
Для начала надо определиться что этот код вообще должен делать? Считать хэшкод один раз? Считать несколько но главное чтобы корректно? Или еще что? А что если хэшкод вернет 0, автор не подумал? Короче я не понимаю идею, что вообще хочется понять то?


Закешировать наверное хотят, но я лично не могу понять действительно ли это выполнится только один раз или имеется ввиду, что выполнится и выполнится, всё равно одно и то же вернёт. На ноль решили забить - Шипилёв чо то такое кстати рассказывал кстати применительно к Strring- реально не большая проблема.

Статья - классика.
автор
Signed by: David Bacon (IBM Research) Joshua Bloch (Javasoft), Jeff Bogda, Cliff Click (Hotspot JVM project), Paul Haahr, Doug Lea, Tom May, Jan-Willem Maessen, Jeremy Manson, John D. Mitchell (jGuru) Kelvin Nilsen, Bill Pugh, Emin Gun Sirer
Не самые последние люди в java сообществе. Не находите?


Статья была написана для java 1.4, я понимаю что ты некромант, но все же. Если computeHashCode вернет 0 - то у этого куда ни при каких раскладах нет шансов выполниться всего лишь раз
19 сен 19, 17:06    [21974500]     Ответить | Цитировать Сообщить модератору
 Re: Почему DCL без volatile работает для 32-bit примитивов ?  [new]
questioner
Member

Откуда:
Сообщений: 1861
забыл ник
questioner
пропущено...


Закешировать наверное хотят, но я лично не могу понять действительно ли это выполнится только один раз или имеется ввиду, что выполнится и выполнится, всё равно одно и то же вернёт. На ноль решили забить - Шипилёв чо то такое кстати рассказывал кстати применительно к Strring- реально не большая проблема.

Статья - классика. пропущено...
Не самые последние люди в java сообществе. Не находите?


Статья была написана для java 1.4, я понимаю что ты некромант, но все же. Если computeHashCode вернет 0 - то у этого куда ни при каких раскладах нет шансов выполниться всего лишь раз


Я ж писал, что интерес академический.

Конкретно в этом примере разница есть 11 это джава или 4-ая ?


забыл ник
Если computeHashCode вернет 0 - то у этого куда ни при каких раскладах нет шансов выполниться всего лишь раз

Да я тебя понял. Вероятность такого хешкода ничтожно мала и можно просто принять тот факт, что для такого хешкода мы будем пересчитывать.
вот кстати String#hashCode из java 11
    public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            hash = h = isLatin1() ? StringLatin1.hashCode(value)
                                  : StringUTF16.hashCode(value);
        }
        return h;
    }
19 сен 19, 17:14    [21974508]     Ответить | Цитировать Сообщить модератору
 Re: Почему DCL без volatile работает для 32-bit примитивов ?  [new]
Андрей Панфилов
Member

Откуда: Москва > Melbourne
Сообщений: 3334
questioner
Я ж писал, что интерес академический.

Конкретно в этом примере разница есть 11 это джава или 4-ая ?
там внизу же написано все: кто final не использует в синглетонах - тот лох, а по факту в java 5 для volatile запретили reordering и статья об этом.
questioner
Да я тебя понял. Вероятность такого хешкода ничтожно мала и можно просто принять тот факт, что для такого хешкода мы будем пересчитывать.
У меня в приложении наверное процентов 10 строк пустые, ничтожность аж зашкаливает.
19 сен 19, 17:26    [21974523]     Ответить | Цитировать Сообщить модератору
 Re: Почему DCL без volatile работает для 32-bit примитивов ?  [new]
забыл ник
Member

Откуда:
Сообщений: 3024
questioner

Конкретно в этом примере разница есть 11 это джава или 4-ая ?



Есть, начиная с 5 java - разработчики дали нам solid JMM, которой нао следовать и это гарантирует что твой код будет работать. В 4-ке такой возможности не было
19 сен 19, 17:28    [21974526]     Ответить | Цитировать Сообщить модератору
 Re: Почему DCL без volatile работает для 32-bit примитивов ?  [new]
Leonid Kudryavtsev
Member

Откуда:
Сообщений: 7927
забыл ник
Если операция идемпотентна и атомарна как в этом случае - то зачем вообще может понадобиться синхронизация?

Слова "идемпотентна" я не знаю )))
А синхронизация в любом случае требуется. Или volatile или synchronize в любом случае должно быть.

IMHO & AFAIK

note: в какой-то момент из примеров кода в данной ветке и volatile и synchronize исчезло. IMHO без них не корректно в любом случае.

забыл ник
Короче я не понимаю идею, что вообще хочется понять то?

Как я понимаю, главные поинт в начальном топике:

Что объекты НЕ атомарны (в "новых" Java вроде для имутабельных объектов сделали исключение) - и такие финты ушами с объектами НЕ прокатывают.

На эту хренотень с объектами на реальном проекте я и сам нарывался, но честно говоря, в голове плохо укладывается. как после физического завершения конструктора объект все еще не проинициализирован. Тут у меня шарики за ролики заезжают и на понятном, "бытовом" языке IMHO это не объяснимо. Надо переставать мыслить терминами "завершилось"/"увидело" и мыслить только абстрактно в терминах "happens before", что лично для меня достаточно тяжело.

IMHO & AFAIK
19 сен 19, 17:33    [21974531]     Ответить | Цитировать Сообщить модератору
 Re: Почему DCL без volatile работает для 32-bit примитивов ?  [new]
Leonid Kudryavtsev
Member

Откуда:
Сообщений: 7927
забыл ник
solid JMM

кидай ссылки на доку, т.к. google например по "solid JMM" находит чисто мусор и зубную пасту (((
19 сен 19, 17:34    [21974537]     Ответить | Цитировать Сообщить модератору
 Re: Почему DCL без volatile работает для 32-bit примитивов ?  [new]
questioner
Member

Откуда:
Сообщений: 1861
Андрей Панфилов
Конкретно в этом примере разница есть 11 это джава или 4-ая ?
там внизу же написано все: кто final не использует в синглетонах [/quot]

Да я тебе больше скажу - кто синглтоны пишет тоже лох.

Андрей Панфилов
У меня в приложении наверное процентов 10 строк пустые, ничтожность аж зашкаливает.


Это тебе к Шипилёву.
19 сен 19, 17:48    [21974556]     Ответить | Цитировать Сообщить модератору
 Re: Почему DCL без volatile работает для 32-bit примитивов ?  [new]
questioner
Member

Откуда:
Сообщений: 1861
[quot Андрей Панфилов]
questioner
там внизу же написано все: кто final не использует в синглетонах - тот лох


http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
If Helper is an immutable object, such that all of the fields of Helper are final, then double-checked locking will work without having to use volatile fields. The idea is that a reference to an immutable object (such as a String or an Integer) should behave in much the same way as an int or float; reading and writing references to immutable objects are atomic.
19 сен 19, 17:53    [21974566]     Ответить | Цитировать Сообщить модератору
 Re: Почему DCL без volatile работает для 32-bit примитивов ?  [new]
Alexey Tomin
Member

Откуда: Самара
Сообщений: 1773
questioner
Второй тред может увидеть, что эта строка выполнилась, но при этом не увидеть предыдущие две строки. Но в этом случае я вообще хз что он может увидеть. По всей видимости тут оптимизации сработать не должны. Только я не понимаю почему.


Треды не видя строки кода, они видят только данные.
Все проблемы синхронизации проще всего описать в терминах двух процессоров с кэшами.
Первый записал значение в КЭШ, второй прочитал свой кэш- и что там у него- фиг знает.

Объясняя неточно, но понятно:
Вход в synchronized секцию и чтение volatile гарантируют, что все наши кэши обновлены.
Выход из synchronized и запись volatile - гарантируют, что мы все кэши сбросили.

Но при этом в варианте с объектом до проверки if (helper == null) нет никакой синхронизации и в "наш" кэш может приехать только ссылка, но не доехать объект (или приехать только его часть). Для этого нужен volatile - он гарантирует, что до нас доедет только всё целиком.

А когда нам нужно только один примитив- то всё просто- он либо есть, либо его нет.
20 сен 19, 10:11    [21974994]     Ответить | Цитировать Сообщить модератору
 Re: Почему DCL без volatile работает для 32-bit примитивов ?  [new]
Lelouch
Member

Откуда: Москва
Сообщений: 1782
questioner
что не надо использовать DCL


А можно уточнить, почему его не надо использовать?
AFAIK в 1.4 он не работал, но после 1.5 в чем проблема?
20 сен 19, 16:37    [21975573]     Ответить | Цитировать Сообщить модератору
Все форумы / Java Ответить