Случается до правила

Java

Случается до правила

1. Введение

Начиная с JDK 5, Java использует новую модель памяти JSR-133, которая использует концепцию «происходит до» для иллюстрации видимости между операциями.

2. Определения

Определение Happens-Before в JSR-133:

Happens-Before RelationshipДва действия могут быть упорядочены отношением "происходит до". Если одно действие> происходит до другого, то первое видно и упорядочено перед вторым. Следует подчеркнуть, что отношение "произошло до" между двумя действиями не означает, что в реализации платформы Java эти действия должны выполняться именно в таком порядке. Отношение "происходит до" в основном подчеркивает порядок между двумя действиями, конфликтующими друг с другом, и определяет, когда происходит гонка данных. Существует несколько способов вызвать событие "происходит до". заказ, в том числе:

  • Each action in a thread happens-before every subsequent action in that thread.
  • An unlock on a monitor happens-before every subsequent lock on that monitor.
  • Запись в volatile-поле происходит перед каждым последующим чтением этого volatile-поля.
  • A call to start() on a thread happens-before any actions in the started thread.
  • All actions in a thread happen-before any other thread successfully returns from a join() on that thread.
  • If an action a happens-before an action b, and b happens before an action c, then a happensbefore c.

определение:Если одно действие происходит раньше другого действия, это означает, что результат первого действия виден второму действию, и порядок выполнения первого действия будет предшествовать второму действию. Существование отношения «происходит до» между двумя операциями не означает, что конкретная реализация платформы Java должна выполняться в порядке, указанном отношением «произошло до». Если результат после переупорядочения согласуется с результатом, полученным в соответствии с отношением «происходит до», то переупорядочение не является незаконным (то есть JMM разрешает такое переупорядочение). Конкретные правила заключаются в следующем:

  • Правила порядка выполнения программы: каждая операция в потоке выполняется до любых последующих операций в этом потоке.
  • Следите за правилами блокировки: отпирание замка происходит до последующего запирания замка.
  • Правило изменчивой переменной: запись в изменчивое поле происходит до любого последующего чтения изменчивого поля.
  • Правила запуска потока: если поток A выполняет операцию ThreadB.start() (запуская поток B), то операция ThreadB.start() потока A происходит до любой операции в потоке B.
  • Правила завершения потока: если поток A выполняет операцию ThreadB.join() и завершается успешно, то любая операция в потоке B выполняется до того, как поток A успешно завершится из операции ThreadB.join().
  • Переходное правило: если происходит - до B, и B бывает - до C, то A происходит - до C.

Примечание: Для уточнения, в Интернете найдено 8 правил. Я не знаю, откуда взялись остальные два. В JSR-133 всего шесть правил. В сети тоже два:

  • Операция прерывания потока: вызов метода прерывания потока () происходит до того, как код прерванного потока обнаружит возникновение события прерывания, и вы можете определить, прерван ли поток с помощью метода Thread.interrupted().
  • Правила финализации объекта: Инициализация объекта завершается до начала метода finalize() объекта.

3. Будьте более конкретными

Правило порядка программы: результат выполнения фрагмента кода в одном потоке соответствует порядку. Внимание результат выполнения, т.к. виртуальная машина и процессор переупорядочат инструкции. Хотя он переупорядочен, он не влияет на результат выполнения программы, поэтому окончательный результат выполнения программы согласуется с результатом последовательного выполнения. Таким образом, это правило распространяется только наодин потокЭффективно, в многопоточной среде не может гарантировать правильность.

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

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

Правило запуска потока: если предположить, что поток A запускает поток B, выполняя ThreadB.start() во время выполнения, то изменение общих переменных потока A гарантированно будет видно потоку B после того, как поток B начнет выполняться.

Правило завершения потока: если предположить, что поток A ожидает завершения потока B, формулируя ThreadB.join() во время процесса выполнения, изменение общих переменных потоком B перед завершением видно после того, как поток A ожидает возврата.

Транзитивное правило: Утверждается, что принцип «происходит до» является транзитивным.

Особо подчеркивается, что случается-поэтому не может быть понято как "временная последовательность". Давайте посмотрим на следующий код:

public class VolatileTest {
    private int a = 0;
    private int getA() {
        return a;
    }
    private void setA(int a) {
        this.a = a;
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 100; i++) {
            VolatileTest volatileTest = new VolatileTest();
            Thread thread1 = new Thread(() -> {
                volatileTest.setA(10);
            });
            thread1.start();

            Thread thread2 = new Thread(() -> {
                System.out.print(volatileTest.getA()+" ");
            });
            thread2.start();
        }
    }
}

Приведенный выше код представляет собой набор простых методов установки/получения Теперь предположим, что есть два потока thread1 и thread2.Поток thread1 сначала выполняет setA(10) (здесь выполняется первым по времени), а затем поток thread2 обращается к getA того же объекта. .(), то каково правильное возвращаемое значение, полученное потоком B в это время?

Ответ: Не уверен

0 0 0 0 10 0 10 10 10 0 10 0 10 10 10 10 10 0 10 10 0 0 0 10 0 10 10 10 0 10 0 10 10 10 0 10 10 0 10 10 10 0 0 10 10 0 10 0 10 10 10 10 10 10 10 10 10 10 0 0 0 10 10 0 10 0 10 0 0 0 10 10 0 10 10 10 10 10 10 10 10 10 10 10 0 10 10 10 0 10 10 10 10 10 0 10 0 10 0 0 

Хотя поток 1 выполняется раньше потока 2 по времени, поскольку код вообще не применяет правило «происходит до», мы не можем определить, какая часть значения получена до потока 2. Другими словами, приведенный выше код небезопасен для потоков.

4. Связь между Happens-Before и JMM

JMM的设计图

Из рисунка видно, что:

  • Правила «происходит до», предоставляемые JMM программистам, могут удовлетворить потребности программистов. Правила JMM «происходит до» не только просты и легки для понимания, но также предоставляют программистам достаточно надежные гарантии видимости памяти (некоторые гарантии видимости памяти могут фактически не существовать, например, вышеприведенное А происходит до Б).
  • Компилятор JMM и процессор были как можно меньше. Как видно из вышеупомянутого анализа, JMM фактически выполняется основным принципом: они не меняют результаты программы (см. Однорезочную программу и правильную многопоточную программу Synchronization), как оптимизировать компилятор и процессор делать. Например, если компилятор, после тщательного анализа, находит блокировку, будет только одноретический доступ, блокировка может быть устранена. Опять же, если компилятор, после тщательного анализа, находит волатильную переменную, будет доступна только один поток, компилятор может привести к обработке этих волатильных переменных в качестве обычной переменной. Эти оптимизации не будут изменить результаты программы, но и улучшают производительность программ.

happens-before与JMM的关系

Правило «происходит до» соответствует одному или нескольким правилам переупорядочения компилятора и процессора. Для программистов Java правило «происходит до» простое и легкое для понимания, оно позволяет программистам Java не изучать сложные правила переупорядочения и конкретные методы реализации этих правил, чтобы понять гарантию видимости памяти, предоставляемую JMM.

5. Резюме и ссылки

резюме

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

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

использованная литература