Учитывая редкую программу, выполняет ли компилятор (чтобы быть очень точным в любой реализации jvm) анализ набора задержек / анализ выхода из потока и т. Д., Чтобы выяснить, какие инструкции по забору необходимо вставить, чтобы сделать его свободным от гонки? или делать JIT делать это в зависимости от того, где он выполняется?
Инструкции по ограничению памяти специфичны для набора команд архитектуры. Они не являются эквивалентными инструкциями в наборе команд JVM. Следовательно, именно JVM / JIT на самом деле выдает инструкции по отбору процессору.
Если компилятор делает это (в данном случае jvm), почему мы не можем просто написать racy-программу, потому что компилятор все равно собирается преобразовать ее в программу без расы? И если компилятор каким-либо образом собирается это сделать (сделать его свободным от гонок, вставив забор), как можно писать неординарные программы (намеренно), например, некоторые реализации параллельных структур данных в Java?
Компилятор будет гарантировать, что при генерации байтового кода все действия, выполняемые над переменными в JVM, подчиняются правилам, указанным в модели памяти Java. В частности, в области оптимизации компилятор может оптимизировать любой набор инструкций, если только он не влияет на отношения между операциями, которые должны существовать между действиями, или на порядок синхронизации между действиями. Например, компилятор не будет реорганизовывать операции чтения и записи по переменным переменным. Это также гарантирует, что отношения «до того» не нарушаются при входе или выходе из охраняемых (синхронизированных) областей кода.
Таким образом, утверждение о том, что компилятор преобразует "racy" -программу в программу без гонки, неверно На самом деле, программа, предполагаемая свободная от гонки (но не в рамках модели памяти Java), может стать «быстрой» после оптимизации.
Параллельные реализации структур данных в Java опираются на гарантии, предоставляемые моделью памяти Java. В частности, это пересмотренная модель памяти Java из Java 5, в которой отношения между операциями чтения и записи изменчивых переменных были заданы точно. Классы ConcurrentXXX в пакете java.util.concurrent
в значительной степени зависят от этого обещанного поведения энергозависимых операций чтения для обеспечения беспроблемного поведения. В модели памяти Java запись в энергозависимую переменную гарантированно происходит перед чтением, если это программный порядок; Проще говоря, энергозависимое чтение всегда извлекает наиболее точную версию данных в переменной. Параллельные классы используют это, чтобы гарантировать, что структуры данных могут обновляться одним потоком, в то время как считываются несколькими другими потоками (в любом другом сценарии могут быть условия гонки).
Или третья возможность, что сам jvm не конвертирует racy в бесплатную программу, но существует другой анализ, который может сделать это для нас. Это так?
JVM выдает инструкции по ограничению памяти. Он не выполняет конвертацию «гоночных» программ в «свободные от гонок». Если компилятор сгенерировал байт-код, который подчиняется модели памяти Java, то JVM / JIT при необходимости выдаст инструкции по ограничению памяти - при чтении / записи изменчивых переменных, получении или освобождении мониторов на объектах и т. Д.
Опасаясь повторения, ни JVM, ни компилятор не преобразуют «гоночную» программу в гоночную, или наоборот. Любое обратное поведение является ошибкой либо в модели памяти Java, либо в JVM. Вам нужно будет написать программу как свободную от гонок, понимая порядок программ, порядок синхронизации и порядок «происходит раньше», а компилятор и JVM гарантируют, что это будет обеспечено во время выполнения.
Я бы рекомендовал вам прочитать эту статью на InfoQ , чтобы получить дополнительную информацию о том, как JVM выдает инструкции по ограничению памяти и гарантирует обещания, сделанные моделью памяти Java.