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();
        }
    }
}
  • Вопрос задан
  • 256 просмотров
Решения вопроса 2
sergey-gornostaev
@sergey-gornostaev Куратор тега Java
Седой и строгий
Гарантия 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 Куратор тега Java
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) ведет к труднуловимым ошибкам.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

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

Похожие вопросы