• Java, многопоточность, инициализация объектов и reordering — всегда ли нужно синхронизировать инициализацию?

    @NikKotovski Автор вопроса
    MaxLich, вы наверное не в курсе, но когда вы комментируете чью-то записть, то человеку автоматом на почту уходит копия ответа. Так что редактирование своего ответа не помогло вам ничего скрыть, скорее наоборот.

    Ну и нет, я не ошибался с адресатом - сообщение было предназначено именно вам.
  • Java, многопоточность, инициализация объектов и reordering — всегда ли нужно синхронизировать инициализацию?

    @NikKotovski Автор вопроса
    MaxLich, Боюсь, вы неправы. Java позволяет компилятору отдать вовне ссылку на созданный, но не инициализированный объект до того, как в нём выполнится код в конструкторе и блоках инициализации. Гарантированно до отдачи ссылки в нём будут инициализированы только поля с пометкой final.

    Но ваш комментарий дважды неверный, потому что абзацом выше я описал, как можно отдать вручную ссылку вовне с помощью слова this. Вы же пишете, что отдача ссылки невозможна, пока конструктор не завершит работу. Ну как так-то?
  • Java, многопоточность, инициализация объектов и reordering — всегда ли нужно синхронизировать инициализацию?

    @NikKotovski Автор вопроса
    Вы имеете ввиду this? По идее, во внешний мир это вернется после конструктора.

    Да, я говорю о возврате во внешний мир. Да, я тоже думал, что ссылка вернётся только после выполнения кода в конструкторе, но большая часть статей/гайдов говорит об обратном. Вот цитата из ссылки, которую я вам дал, например:
    The most obvious reason it doesn't work it that the writes that initialize the Helper object and the write to the helper field can be done or perceived out of order. Thus, a thread which invokes getHelper() could see a non-null reference to a helper object, but see the default values for fields of the helper object, rather than the values set in the constructor.

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

    За название книги спасибо - попробую поискать там.
  • Java, многопоточность, инициализация объектов и reordering — всегда ли нужно синхронизировать инициализацию?

    @NikKotovski Автор вопроса
    Подумайте сами, как может конструктор отработать на половину?

    Мммммммм, никто и не говорил, что конструктор может отрбать наполовину. Речь о том, что он может передать ссылку на объект вовне до того, как пройдёт инициализация полей. Память под них конечно уже будет выделена, просто значения будут по-умолчанию. И конструктор также отработает полностью, никакой половинчатости, просто ссылка уже будет в наличии.
    Такое поведение либо напрямую описанно во всех статьях об инициализации объектов, которые я встречал, либо следует из них. Например тут прямым текстом:
    www.cs.umd.edu/~pugh/java/memoryModel/DoubleChecke...
    https://stackoverflow.com/questions/39393522/synch...

    Конечно с дуру можно и член сломать, но зачем Вам в конструкторе передавать указатель на себя еще куда-то?

    Ну например если у меня есть статический список или map со всем экземплярами данного класса, объявленный внутри этого же класса. Но вопрос не об этом совершенно - о this в конструкторе я как раз написал, чтобы было понятно, что я не имею в виду описанную bad practice.

    По Вашему примеру рождается вопрос, не идет ли речь про Singleton? Если да, то 2 из 4 способов реализации этого паттерна описанные в книге Джошуа Блоха потокобезопасные.

    Да, по сути вопрос не сильно отличается от реализации Singleton. Но я как раз и пытаюсь понять, почему он реализован именно так, а не иначе. К тому же когда была написана эта книга, потому что:
    Проблема в том, что большая часть из ответов старые и логика синхронный работы с тех пор менялась раз или два. Более того, среди более свежих заметок по этой теме мне уже встречались обе точки зрения - и я не могу понять, то ли те несколько заметок, которые говорят, что это безопасно, ошибочны, то ли ошибается большинство, которое по инерции считает, что инициализация потоково-небезопасна.


    Суть volatile в том, что Вы говорите не кешировать это значение в процессоре, а всегда спускаться в память

    Да, я знаю как работает volatile. Более того вы не правы насчёт атомарности. Volatile ГАРАНТИРУЕТ, что чтение и запись в переменную будут атомарными,
    https://stackoverflow.com/a/3038233
    А с версии 1.5 также ещё влияет на код вокруг. Например запись в volatile также гарантирует, что при чтении и записи в выражений, прописанных в блоке до неё, не будет меняться порядок.

    Проблема в том, что JVM, как я понимаю, сначала создаёт ссылку на объект и помещает её в переменную, потом же выполняет код в конструкторе и блоках инициализации. Вопрос в том, считается ли это одной операцией или нет. Как мне кажется - нет, и volatile тут никак не поможет. Но я не могу быть уверен.
  • Java, многопоточность, инициализация объектов и reordering — всегда ли нужно синхронизировать инициализацию?

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

    С экспериментальными данными тяжело, потому что на одной системе оно может сработать, а на другой нет. Даже банальный reordering довольно тяжело поймать. Хоть тут у нас получается гонка, её поймать попроще. Но опять же, если мы её увидим - всё будет сразу ясно, а вот если нет, то тут уже результат будет неоднозначный.
    Тем не менее проблема описанная в 3м примере точно имеет место быть, про второй и третий пример в интернете же написаны десятки, если не сотни статей и заметок, например:
    www.cs.umd.edu/~pugh/java/memoryModel/DoubleChecke...
    https://en.wikipedia.org/wiki/Double-checked_locki...

    Ну и мой вопрос немного о другом. Как я понимаю создание объекта в Java идёт в 2 этапа - сначала генерируется ссылка на объект и записывается в переменную, потом уже выполняется сам конструтор, и это считается двумя отдельными операциями, поэтому между ними может влезть другой поток. Меня же интересует так ли это и можно ли эту проблему обойти созданием метода или ключевым словом volatile, потому что пошагово объяснения механизма проблемы я нигде найти так и не смог. Т.е. проблема описано много где, но подробно почему она возникает почему-то не расписывает никто. Это кстати, ведёт к другой проблеме: т.к. механизм нигде не расписан (ну или по крайней мере не гуглится), то каждый начинает понимать проблемы синхронной инициализации по-своему, и появляется много статей и ответов на stackoverflow, которые противоречат друг другу и тем вещам, которые я смог расписать.