Демонстрация кода в неконечном поле, прочитанном перед инициализацией в многопоточной среде - PullRequest
0 голосов
/ 12 апреля 2020

Неправильное значение поля может быть неправильным

class FinalFieldExample { 
    final int x;
    int y; 
    static FinalFieldExample f;

    public FinalFieldExample() {
        x = 3; 
        y = 4; 
    } 

    static void writer() {
        f = new FinalFieldExample();
    } 

    static void reader() {
        if (f != null) {
            int i = f.x;  // guaranteed to see 3  
            int j = f.y;  // could see 0
        } 
    } 
}

Я разделяю ту же путаницу, что и автор этого вопроса, читая главу 17 JLS о JMM. Я могу буквально понять, что в описанной ситуации fy может быть 0. Но это все равно, что читать правило, не задумываясь об этом.

Было бы очень трудно запомнить правило, если бы я не видел ошибку, вызванную несоблюдением правила. Я искал net, но не могу найти никого, кто привел пример для ситуации, когда fy может быть 0, и я не могу привести мой пример.

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

1 Ответ

0 голосов
/ 12 апреля 2020

Следующий пример показывает, когда y может быть 0.

class FinalFieldExample {

    final int x;
    int y;
    static FinalFieldExample f;


    FinalFieldExample() {
        x = 3;
        f = this;
        for (int i = 0; i < 1000000; i++) {
            // simulate processing
        }
        y = 4;
    }


    static void writer() {
        new FinalFieldExample();
    }


    static void reader() {
        System.out.println(f.x); // guaranteed to see 3
        System.out.println(f.y); // could see 0
    }


    public static void main(String[] args) {
        new Thread(FinalFieldExample::writer).start();
        new Thread(FinalFieldExample::reader).start();
    }
}

Обратите внимание, что это не вызвано повторным вызовом инициализации компилятором JIT. Проблема возникает из-за того, что this был опубликован или, скорее, стал видимым для других потоков во время создания. Здесь - это пост о той же документации JLS и похожем примере.

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

Детали того, что JIT может или будет делать, недетерминированы c. Просмотр миллионов образцов прогонов не даст значимого шаблона, поскольку переупорядочения носят субъективный характер и зависят от очень специфических деталей c, таких как дуга процессора, время, эвристика, размер графика, поставщик JVM, размер байт-кода и т. Д. c. Мы знаем только, что JIT будет предполагать, что код выполняется в однопоточной среде, когда ему не нужно соответствовать JMM. В конце концов, JIT мало что значит для вашего многопоточного кода.

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

Здесь и здесь некоторые люди создали примеры, которые показывают переупорядочение. Вы можете видеть, что это очень низкий уровень i sh эффекта. Документация только говорит, что это может случиться, но не объясняет когда . Вы не должны воспринимать это как «правило», о котором вам нужно знать. Это просто дополнительная информация для вас.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...