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

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

Войти через центр авторизации
Похожие вопросы
Zaycev.net Челябинск
от 65 000 до 100 000 руб.
Nexign Санкт-Петербург
от 100 000 руб.
timebook Нижний Новгород
от 120 000 руб.