Прочитав немного о модели памяти java и синхронизации, возникло несколько вопросов:

Даже если поток 1 синхронизирует записи, тогда, хотя эффект записи будет сброшен в основную память, поток 2 все равно не увидит их, потому что чтение было выполнено из кеша уровня 1. Таким образом, синхронизация записи предотвращает конфликты только при записи. (поточно-безопасная хэш-карта Java только для записи)

Во-вторых, когда синхронизированный метод завершается, он автоматически устанавливает связь «произошло до» с любым последующим вызовом синхронизированного метода для того же объекта. Это гарантирует, что изменения состояния объекта будут видны всем потокам. (https://docs.oracle.com/javase/tutorial/essential /concurrency/syncmeth.html)

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

Мои вопросы:

  1. Что действительно сбрасывается обратно в память при выходе из синхронизированного блока? (Поскольку некоторые веб-сайты также заявили, что будет сброшен только объект, блокировка которого была получена.)

  2. Что в этом случае означает происходит до отношений ? А что будет перечитываться из памяти при входе в блок, а что нет?

  3. Как блокировка обеспечивает эту функциональность (из https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Lock.html):

    Все реализации блокировки должны обеспечивать ту же семантику синхронизации памяти, что и встроенная блокировка монитора, как описано в разделе 17.4 спецификации языка Java ™:

    <цитата>

    Успешная операция блокировки имеет те же эффекты синхронизации памяти, что и успешная операция блокировки. Успешная операция разблокировки имеет те же эффекты синхронизации памяти, что и успешная операция разблокировки. Неудачные операции блокировки и разблокировки и повторные операции блокировки / разблокировки не требуют каких-либо эффектов синхронизации памяти.

Если мое предположение, что все будет перечитано и сброшено, верно, это достигается за счет использования synchronized-block в функциях блокировки и разблокировки (которые в большинстве случаев также необходимы), верно? И если это не так, то как добиться этой функциональности?

Заранее спасибо!

0
Quaffel 31 Дек 2017 в 13:58

2 ответа

Лучший ответ

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

Если поток выполняет действие A в synchronized(object1) { … }, за которым следует поток, выполняющий действие B в synchronized(object1) { … }, при условии, что object1 относится к тому же объекту, между A и B существует связь произошло до , и эти действия безопасны в отношении доступа к совместно используемым изменяемым данным (при условии, что никто другой не изменяет эти данные).

Но это направленные отношения, то есть B может безопасно получить доступ к данным, измененным A. Но когда вы видите два блока synchronized(object1) { … }, будучи уверенным, что object1 - это один и тот же объект, вам все равно нужно знать, был ли A выполнен до того, как B или B был выполняется до A, чтобы узнать направление отношения "происходит до" . Для обычного объектно-ориентированного кода это обычно работает естественно, поскольку каждое действие будет работать с любым предыдущим состоянием найденного объекта.

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

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

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


Переменные volatile, как и используемые Lock внутри, также имеют глобальный эффект сброса, если потоки читают и записывают одну и ту же переменную volatile и используют значение для формирования правильной логики программы. . Это сложнее, чем с блоками synchronized, так как нет взаимного исключения выполнения кода, или, ну, вы могли бы видеть это как наличие взаимного исключения, ограниченного одной операцией чтения, записи или cas.

3
Holger 3 Янв 2018 в 16:57

Само по себе flush не существует, просто так думать проще (тоже легче рисовать); вот почему в Интернете есть много ресурсов, которые относятся к сбросу в основную память (предполагается, что это ОЗУ), но на самом деле это происходит не так часто. На самом деле происходит слив буферов загрузки и / или хранения в кэш L1 (L2 в случае IBM), и синхронизация данных оттуда выполняется протоколом согласования кеша; или, другими словами, кеши достаточно умны, чтобы общаться друг с другом (через шину) и не извлекать данные из основной памяти все время.

Это сложная тема (отказ от ответственности: хотя я стараюсь много читать об этом, много тестов, когда у меня есть время, я совершенно не понимаю это в полной мере), это касается потенциального компилятора / процессора / и т. -заказы (порядок программ никогда не соблюдается), это касается сброса буферов, барьеров памяти, семантики выпуска / получения ... Я не думаю, что на ваш вопрос можно ответить без отчета phD; вот почему в JLS есть более высокие уровни, называемые - «происходит до».

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

Что действительно сбрасывается обратно в память при выходе из синхронизированного блока

Наверное, вообще ничего - кеши «разговаривают» друг с другом для синхронизации данных; Я могу думать только о двух других случаях: когда вы впервые читаете некоторые данные и когда поток умирает - все записанные данные будут сброшены в основную память (но я не уверен).

Что в этом случае означает «произошло до отношений»? А что будет перечитываться из памяти при входе в блок, а что нет?

Действительно, та же фраза, что и выше.

Как замок выполняет эту функцию

Обычно путем введения барьеров памяти; как и летучие вещества.

0
Eugene 3 Янв 2018 в 21:23