Подтвердить что ты не робот

Безопасная публикация и преимущество непреложного по сравнению с фактически неизменяемым

Я перечитываю Java Concurrency На практике, и я не уверен, что полностью понимаю главу о неизменности и безопасной публикации.

Что говорит книга:

Неизменяемые объекты могут безопасно использоваться любым потоком без дополнительных  синхронизации, даже если синхронизация не используется для публикации  их.

Я не понимаю, почему кто-нибудь (заинтересованный в правильном написании своего кода) публикует некоторые ссылки ненадежно?

Если объект неизменен и он опубликован небезопасно, я понимаю, что любой другой поток, получающий ссылку на объект, будет видеть его правильное состояние из-за гарантий, предлагаемых надлежащей неизменностью (с полями final и т.д.),

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

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

Безопасно опубликованные эффективно неизменяемые объекты могут безопасно использоваться любой поток без дополнительной синхронизации.

Итак, почему неизменность (против эффективной неизменности) так важна? В каком случае можно было бы искать небезопасную публикацию?

4b9b3361

Ответ 1

Желательно создавать объекты, которые не нуждаются в синхронизации по двум причинам:

  • Пользователи ваших объектов могут забыть синхронизировать.
  • Несмотря на то, что накладные расходы очень малы, синхронизация не является бесплатной, особенно если ваши объекты не используются часто и разными потоками.

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

Также помните, что автор не говорит, что объект опубликован без предупреждения, он безопасно опубликован без синхронизации.

Что касается вашего второго вопроса, я только что проверил, и книга не обещает вам, что в другом потоке всегда будет отображаться ссылка на обновленный объект, только если это произойдет, он увидит полный объект. Но я могу представить, что если он будет опубликован через конструктор другого объекта (Runnable?), Он будет приятным. Однако это помогает объяснить все случаи.

ИЗМЕНИТЬ:

эффективно неизменяемый и неизменный Разница между фактически неизменяемыми и неизменными заключается в том, что в первом случае вам все равно нужно публиковать объекты безопасным способом. Для действительно неизменяемых объектов это не требуется. Поэтому действительно неизменяемые объекты предпочтительнее, потому что их легче опубликовать по причинам, указанным выше.

Ответ 2

Итак, почему неизменность (против эффективной неизменности) так важна?

Я думаю, что основной момент заключается в том, что поистине непреложные объекты сложнее сломать позже. Если вы объявили поле final, то оно final, period. Вам нужно будет удалить final, чтобы изменить это поле, и это должно вызвать тревогу. Но если вы изначально вышли из final, кто-то может небрежно просто добавить код, который изменит поле, а бум - вы завинчены - только с добавленным кодом (возможно, в подклассе), без изменений существующего кода.

Я также предполагал бы, что явная неизменность позволяет компилятору (JIT) выполнять некоторые оптимизации, которые в противном случае были бы трудными или невозможными для оправдания. Например, при использовании полей volatile среда выполнения должна гарантировать связь с записью и чтением. На практике это может потребовать барьеров памяти, отключения оптимизации выполнения вне порядка и т.д., То есть производительности. Но если объект (глубоко) неизменен (содержит только final ссылки на другие неизменяемые объекты), требование может быть ослаблено без нарушения чего-либо: связь между случаями и данными должна быть гарантирована только при записи и чтении одной единственной ссылки, а не весь граф объекта.

Таким образом, явная неизменность делает программу более простой, так что человеку проще и удобнее рассуждать, поддерживать и упрощать работу компьютера. Эти преимущества растут экспоненциально по мере роста графа объекта, т.е. Объекты содержат объекты, содержащие объекты - все это просто, если все неизменно. Когда необходима изменчивость, локализация его в строго определенных местах и ​​сохранение остального неизменного по-прежнему дает много преимуществ.

Ответ 3

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

Я считаю, что разница в том, что внутреннее состояние эффективно неизменяемых объектов можно наблюдать в несогласованном состоянии, когда они не публикуются безопасно, тогда как внутреннее состояние неизменяемых объектов никогда не может наблюдаться в противоречивом состоянии.

Однако я считаю, что ссылка на неизменяемый объект может быть устаревшей/устаревшей, если ссылка не опубликована безопасно.

Ответ 4

"Небезопасная публикация" часто уместна в тех случаях, когда желательно, чтобы другие потоки видели последнее значение, записанное в поле, но наличие потоков с более ранним значением было бы относительно безвредным. Ярким примером является кеш-хэш-значение для String. В первый раз hashCode() вызывается на String, он будет вычислять значение и кэшировать его. Если другой поток, который вызывает hashCode() в той же строке, может видеть значение, вычисленное первым потоком, ему не нужно будет пересчитывать значение хэша (таким образом, экономя время), но ничего плохого не произойдет, если второй поток не будет см. значение хэша. Это просто закончит выполнение избыточного, но безвредного вычисления, которого можно было бы избежать. Имея hashCode() публикацию хэш-значения безопасно было бы возможно, но случайные избыточные вычисления хэша намного дешевле, чем синхронизация, необходимая для безопасной публикации. Действительно, за исключением довольно длинных строк, затраты на синхронизацию, вероятно, отрицают какую-либо выгоду от кеширования.

К сожалению, я не думаю, что создатели Java представляли себе ситуации, когда код записывал в поле и предпочитал, чтобы он был видимым для других потоков, но не слишком сильно, если это не так, и где хранится ссылка к полю, в свою очередь, идентифицирует другой объект с аналогичным полем. Это приводит к ситуации, когда написание семантически-правильного кода гораздо более громоздко и, вероятно, медленнее кода, который, вероятно, будет работать, но семантика которого не будет гарантирована. Я не знаю никаких действительно хороших средств для этого в некоторых случаях, кроме использования каких-то безвозмездных полей final, чтобы обеспечить правильную публикацию вещей.