Эффекты памяти синхронизированного ключевого слова в Java

Возможно, на этот вопрос уже был дан ответ, но из-за сложности вопроса мне нужно подтверждение. Поэтому я перефразирую вопрос

Вопрос 1. Когда поток входит в синхронизированный блок, барьер памяти будет включать любые затронутые поля, а не только поля объекта, который я синхронизировал? Таким образом, если внутри синхронизированного блока изменяется много объектов, это означает, что много памяти перемещается между кэшами памяти потоков.

Thread 1
object.field1 = "";
synchronized (lock) {
  farAwayObject.field1 = "";
  farAwayObject.evenFarther.field2 = "";
}

Thread 2. assuming thread ordering is correct
synchronized (lock) {
  //thread 2 guaranteed to see all fields above as ""
  //even object.field1 ?
}

Вопрос 2. Является ли object.field1 = ""; в потоке 1 неявной частью отношения "происходит до"?

Я надеюсь, что это так, но это может быть не так. Если нет, то есть ли способ сделать это, не помещая его в блок синхронизации? Трудно рассуждать о программе иначе, и нецелесообразно размещать все под синхронизированным { }.

РЕДАКТИРОВАТЬ: уточнение: object.field1 не является изменчивым, и вопрос заключается в том, «будет ли поток 2 гарантированно видеть запись потока 1, по крайней мере». Мой вопрос касается видимости памяти. В качестве аргумента предположим, что только поток 1 записывает в энергонезависимый object.field1.

Вопрос 2 можно перефразировать так:

«Будет ли синхронизированный блок в блокировке выталкивать изменения, сделанные ранее, чтобы их могли увидеть другие потоки, синхронизирующиеся в той же блокировке?»


person Mordan    schedule 08.02.2017    source источник
comment
Ответ положительный на оба вопроса.   -  person shmosel    schedule 08.02.2017
comment
@shmosel У меня было понимание, что object.field1 не будет участвовать в событиях-раньше, а Thread2 может или не может увидеть обновление?   -  person CKing    schedule 08.02.2017


Ответы (3)


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

Предполагая, что поля farAwayObject и evenFarther всегда изменяются и доступ к ним осуществляется путем получения lock для одного и того же объекта во всем приложении, все потоки всегда будут видеть обновления, сделанные для farAwayObject и evenFarther, поскольку synchronized применяет происходит-до состояние.

//thread 2 guaranteed to see all fields above as ""

То же самое нельзя сказать о object.field1, не зная, как оно было объявлено. Если предположить, что field1 является ссылкой, не помеченной как volatile, она не будет частью отношения происходит до, и потоки могут видеть для нее устаревшие значения.

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

Да. Отметить object.field1 как volatile.


Адресация вашего редактирования:

Вопрос 2 можно перефразировать как

«Будет ли синхронизированный блок в блокировке выталкивать изменения, сделанные ранее, чтобы их могли увидеть другие потоки, синхронизирующиеся в той же блокировке?»

На мой взгляд, ответ «да», при условии, что потоки записи получают блокировку перед потоками чтения. К сожалению, это то, что вы обычно не можете гарантировать, и поэтому object.field1 необходимо пометить как volatile или оператор должен быть перемещен внутрь блока synchronized.

Взгляните на JSR 133, который говорит о том, что кеш сбрасывается в основную память, когда поток выходит из блока syncronized. Это должно еще больше прояснить ситуацию.


person CKing    schedule 08.02.2017
comment
см. мое разъяснение. Если я не хочу устанавливать object.field1 как volatile. не является ли конкретная запись thread1 в object.field1 перенесенной в основную память при выполнении синхронизированной (блокировки)? Если нет. будет ли это означать, что JVM помечает изменения памяти, сделанные под синхронизированной блокировкой? чтобы только подтолкнуть эти изменения? - person Mordan; 08.02.2017
comment
@Mordan Смотрите редактирование. Это проясняет ситуацию? Дайте мне знать, если это нуждается в дополнительных разъяснениях. - person CKing; 08.02.2017
comment
ваша ссылка, видимо, ответила на мой вопрос. Это да. Мне полегчало. :) Я искал в Интернете несколько недель, но так и не нашел. Отличная ссылка! благодарю вас. - person Mordan; 08.02.2017
comment
@Mordan Рад быть полезным. Я полагаю, что мой ответ имел бы больше смысла, если бы я говорил о synchronized кеше сбрасывания блоков при выходе, прежде чем говорить о чем-либо еще. Под «да» вы подразумеваете, что другие потоки всегда будут видеть обновленное значение object.field?. Это неправда, как объяснено в моем ответе. - person CKing; 08.02.2017
comment
Да.. При условии, что другие потоки не пишут в него, поток 2 всегда будет видеть в object.field, потому что он синхронизируется с той же блокировкой. Ваша ссылка предоставила другие драгоценные камни. Запись в энергозависимое поле имеет тот же эффект памяти, что и освобождение монитора, а чтение из энергозависимого поля имеет тот же эффект памяти, что и захват монитора. Фактически, поскольку новая модель памяти накладывает более строгие ограничения на переупорядочивание доступа к изменчивому полю с доступом к другим полям, изменчивым или нет, все, что было видно потоку A, когда он записывает в изменчивое поле f, становится видимым потоку B, когда он читает f. - person Mordan; 08.02.2017
comment
Эти комментарии доказывают, что тема сложная. Согласны ли вы с тем, что если другие потоки не будут писать в него, и при условии, что поток 2 получит блокировку после потока 1, поток 2 всегда будет видеть в object.field, поскольку он синхронизировался с той же блокировкой? @СКинг - person Mordan; 08.02.2017
comment
@ Мордан Да. Ваш последний комментарий верен. Это то, что я объяснил в своем ответе. - person CKing; 08.02.2017
comment
Хорошо, тогда мы согласны :). В моем случае я могу убедиться, что поток 2 не запускается до получения блокировки. Он ждет. - person Mordan; 08.02.2017

1) Когда поток входит в синхронизированный блок, барьер памяти будет включать любые затронутые поля, а не только поля объекта, который я синхронизировал?

Правильный. (Предположим, что поток 1 и поток 2 синхронизируются с одной и той же блокировкой.)

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

Потенциально, да. Однако это (вероятно) не перемещение между тайниками. Скорее всего, это перемещение из кэша одного процессора в память, а из памяти в кэш второго процессора. Конечно, это зависит от того, как аппаратно реализуется иерархия памяти.

2) Есть object.field1 = ""; в потоке 1 неявно часть отношения «происходит до»?

Есть цепочка событий-прежде отношений

  1. Запись в object.field1 происходит до получения блокировки.
  2. Это происходит перед записью в farAwayObject и так далее.
  3. Это происходит до того, как блокировка будет снята потоком 1.
  4. Это происходит до того, как блокировка будет получена потоком 2.
  5. Это происходит до того, как поток 2 прочитает object.field1.

Проблема заключается в том, что происходит, если происходит промежуточная запись в object.field1 либо до того, как lock будет получен потоком 1, либо каким-либо другим потоком. В любом из этих случаев цепочки «происходит до» недостаточно, чтобы гарантировать, что поток 2 увидит значение, записанное потоком 1.

person Stephen C    schedule 08.02.2017
comment
не могли бы вы пояснить в свете моего разъяснения. предположим, что нет промежуточных операций записи. Вы бы ответили да, как шмосель? - person Mordan; 08.02.2017

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

Да.

Объект.field1 = ; в потоке 1 неявно часть отношения «происходит до»?

Да, даже если он не энергозависимый.


Порядок «происходит до» — это частичный порядок.

Порядок «происходит до» задается транзитивным замыканием синхронизируемых с ребрами и программным порядком. Это должен быть допустимый частичный порядок: рефлексивный, транзитивный и антисимметричный.

(JLS 17.4.7< /а>)

Действия перед границей синхронизации (т. е. освобождение синхронизированной блокировки) упорядочены по порядку программы и, таким образом, синхронизированы с освобождением. Транзитивность говорит, что действия, упорядоченные приобретением той же блокировки в другом потоке, поэтому имеют порядок «происходит до» как с освобождением этой блокировки, так и с действиями, предшествующими освобождению этой блокировки, независимо от того, не внутри тела блока synchronized. Важно помнить, что упорядочение происходит в действиях (т. е. в получении/снятии блокировки), а не в блоке, как подразумевается скобками ключевого слова synchronized. Скобки указывают положение действий получения/освобождения, а также место, где набор действий не может чередоваться.

И, наконец, помните, что бывает — раньше — это частичный порядок. Это означает:

  1. Происходит до обеспечивает согласованность памяти, когда действия происходят в определенном порядке (например, освобождение/получение, запись/чтение и т. д.)
  2. Случается до того, как зависит от более сильных гарантий, таких как порядок программы для создания правильной функциональности.
  3. Происходит раньше, не предотвращает ошибки, возникающие из-за чередования неатомарных действий.
  4. Случается раньше — это переходная связь между действием и сильным действием. (Вы можете поместить чтение вашей общей переменной вне блока synchronized, если запись идет до блока, а чтение идет после блока). Перед заказом также следуют более сильные гарантии заказа, но они также обеспечивают дополнительные эффекты, описанные в спецификации.
person Community    schedule 08.02.2017