Происходит до отношений с изменчивыми полями и синхронизированными блоками в Java – и их влияние на энергонезависимые переменные?

Я до сих пор довольно новичок в концепции streamовой обработки и пытаюсь понять больше об этом. Недавно я наткнулся на сообщение в блоге « Что летучие средства на Java » Джереми Мэнсона, где он пишет:

Когда один stream записывает в изменчивую переменную, а другой stream видит, что пишет, первый stream сообщает второй обо всем содержимом памяти до тех пор, пока она не выполнит запись в эту изменчивую переменную. […] все содержимое памяти, видимое в Thread 1, прежде чем оно будет написано в [volatile] ready , должно быть видимым для Thread 2 после того, как оно прочитает значение true для ready . [выделено мной мной]

Итак, означает ли это, что все переменные (volatile или нет), хранящиеся в памяти Thread 1 во время записи в переменную volatile, станут видимыми для Thread 2 после того, как она прочитает эту изменчивую переменную? Если это так, можно ли выдумать эту инструкцию вместе с официальными документами Java / Oracle? И из какой версии Java он будет работать?

В частности, если все streamи разделяют следующие переменные classа:

 private String s = "running"; private volatile boolean b = false; 

И Thread 1 выполняет следующее:

 s = "done"; b = true; 

Затем Thread 2 выполняет (после того, как Thread 1 написал в поле volatile):

 boolean flag = b; //read from volatile System.out.println(s); 

Будет ли гарантирована печать «сделано»?

Что произойдет, если вместо объявления b как volatile я помещу запись и чтение в synchronized блок?

Кроме того, в дискуссии, озаглавленной « Связаны ли статические переменные между streamами? », @TREE пишет :

Не используйте volatile для защиты более чем одной части общего состояния.

Зачем? (Извините, я еще не могу прокомментировать другие вопросы, или я бы спросил там …)

    Да, гарантируется, что stream 2 напечатает «done». Конечно, то есть, если запись в b в Thread 1 фактически происходит до чтения из b в Thread 2, а не в одно и то же время или раньше!

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

    В вашем случае у вас есть следующие события:

    • Тема 1 записывает в s
    • Тема 1 записывается в b
    • Тема 2 читает из b
    • Тема 2 читает с s

    И следующие правила вступают в игру:

    • «Если x и y – действия одного и того же streamа, а x идет до y в порядке программы, тогда hb (x, y)». (правило заказа программы )
    • «Запись в изменчивое поле (§8.3.1.4) происходит – перед каждым последующим чтением этого поля». ( изменчивое правило)

    Происходит следующее: до того, как существуют отношения:

    • Поток 1 пишет в s происходит до того, как Thread 1 пишет в b (правило порядка программы)
    • Поток 1 записывается в b , прежде чем Thread 2 читает из b (volatile rule)
    • Thread 2 читается с b , до того, как Thread 2 читает s (правило порядка программы)

    Если вы следуете этой цепочке, вы можете увидеть, что в результате:

    • Поток 1 пишет в s происходит до того, как Thread 2 читает с s

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

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

    Не используйте volatile для защиты более чем одной части общего состояния.

    Зачем?

    volatile не гарантирует атомарность: в вашем примере переменная s также может быть изменена другими streamами после записи, которую вы показываете; stream чтения не будет иметь никаких гарантий относительно того, какое значение он видит. То же самое происходит с тем, что записи происходят после того, как вы читаете информацию об volatile , но перед чтением s .

    То, что безопасно делать и делается на практике, разделяет неизменяемое состояние, транзитно доступное из ссылки, написанной на volatile переменную. Возможно, это смысл, обозначаемый «одним куском общего состояния».

    возможно ли свести эту формулировку вместе с официальными документами Java / Oracle?

    Цитаты из спецификации:

    17.4.4. Порядок синхронизации

    Запись в изменчивую переменную v (§8.3.1.4) синхронизируется со всеми последующими чтениями v любым streamом (где «последующий» определяется в соответствии с порядком синхронизации).

    17.4.5. Бывает-до заказа

    Если x и y – действия одного и того же streamа, а x – до y в программном порядке, то hb (x, y).

    Если действие x синхронизируется со следующим действием y, то мы также имеем hb (x, y).

    Этого должно быть достаточно.

    И из какой версии Java он будет работать?

    Спецификация Java Language Specification, 3rd Edition представила переписку спецификации модели памяти, которая является ключом к вышеуказанным гарантиям. NB большинство предыдущих версий действовали так, как если бы гарантии были там, и многие строки кода на самом деле зависели от него. Люди были удивлены, когда узнали, что гарантий на самом деле там не было.

    Будет ли гарантирована печать «сделано»?

    Как сказано в Java Concurrency in Practice :

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

    Итак, ДА , это гарантирует печать «сделано».

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

    Это тоже гарантирует то же самое.

    Не используйте volatile для защиты более чем одной части общего состояния.

    Зачем?

    Потому что волатильность гарантирует только видимость. Это не гарантирует атомарность. Если у нас есть две летучие записи в методе, к которому обращается stream A а другой stream B обращается к этим изменчивым переменным, тогда, когда stream A выполняет этот метод, возможно, что stream A будет вытеснен streamом B в середине операций (например, после первой волатильной записи, но до второй летучей записи по streamу A ). Таким образом, гарантирование атомарности synchronization операций является наиболее осуществимым выходом.

    Давайте будем гением компьютера.