codefucker
@codefucker
Интернет-наркоман

Почему этот код выкидывает RuntimeException, не смотря на volatile?

Изучаю многопоточность в Java. Насколько я понял, в JMM есть гарантия, что после записи в volitile, все операции до обязаны выполниться. Т.е. если при чтении в другом потоке y равен 2, то x обязан быть 1. Но так не происходит. ЧЯДНТ?

public class Reordering {
    int x = 0;
    volatile int y = 0;

    public void writer() {
        x = 1;
        y = 2;
    }

    public void reader() {

        if (y == 2) {
            if(x == 0) {
                throw new RuntimeException();
            }

            this.x = 0;
            this.y = 0;
        }
    }

    public static void main(String[] args) {
        final Reordering reordering = new Reordering();

        final Thread thread = new Thread(() -> {
            while (true) {
                reordering.writer();
            }
        });

        thread.setDaemon(true);
        thread.start();

        while (true) {
            reordering.reader();
        }
    }
}
  • Вопрос задан
  • 198 просмотров
Решения вопроса 2
  • TheDeadOne
    @TheDeadOne
    Седой и строгий
    Гарантия happens-before не исключает гонок

    reader     writer  состояние
    ---------- ------ ------------
    перед x=0          x=1, y=2
                 x=1   x=1, y=2
    x=0                x=0, y=2
    y=0                x=0, y=0
                 y=2   x=0, y=2

    При следующем вызове reader он получит состояние в котором x равно 0, когда y равно 2.
    Ответ написан
  • zagayevskiy
    @zagayevskiy
    Android developer at Yandex
    volatile ничего не говорит ни про что кроме переменной, на которой он поставлен. Говорит, что это переменная не будет закеширована в тренде. Ты всегда будешь видеть её актуальное значение. У тебя реализована банальная гонка потоков. х может браться из локального кеша (thread local) - спорно и в данном случае неважно.

    Распишу подробнее гонку. Код может выполняться в такой последовательности

    x = 1; //thread 1
    x = 0; //thread 2
    y = 0; //thread 2
    y = 2; //thread 1
    if (y == 2) {  //thread 1, TRUE
        if (x == 0) { //thread 1, TRUE
            throw //thread 1, привет
        }
    }


    То есть это всё о чём - одно нахождение переменной и её присвоений рядом с volatile переменной ничего не гарантирует. Никакой синхронизации нет. Отличие от кода, который есть в статье, которую ты привел в том, что там меняет данные лишь один поток, поэтому гонки не возникает.
    Кроме того, знание таких мелочей скорее вредно, потому что может возникнуть желание использовать это в реальном коде, что ведёт к опасному коду, малейшее изменение которого(или, не дай бог, JMM) ведет к труднуловимым ошибкам.
    Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через TM ID
Похожие вопросы