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) ведет к труднуловимым ошибкам.