Это правильно? Если да, то где в документации по Java это сказано?
Да, это верно - спецификации Java говорят это в JLS 17.4.5. Происходит до:
В приведенном вами примере между двумя нитями идет гонка. Кроме того, чтобы поток 2 мог видеть, что сделал поток 1, поток 2 должен прочитать x
(переменная) перед чтением y
. Если thread2 читает только y
, что не является энергозависимым, тогда видимость измененного значения y
не гарантируется.
Вы можете изменить пример кода, как показано ниже. Гонка еще продолжается, поэтому результат может быть x=0 y=0
, x=0 y=1
или x=1 y=1
. Но использование volatile гарантирует, что программа никогда не выдаст x=1 y=0
.
public class UseVolatile {
public static void main(String[] args) {
Shared shared = new Shared();
new Thread(() -> shared.setOne()).start();
new Thread(() -> {
int x = shared.getX();
int y = shared.getY();
System.out.printf("x=%d y=%d%n", x, y);
}).start();
}
}
class Shared {
private int y = 0;
private volatile int x = 0;
public void setOne() {
y = 1;
x = 1;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
Редактировать
Мы можем понять, почему вывод x=1 y=0
может 'это случилось. Ссылаясь на JLS:
Два действия могут быть упорядочены с помощью отношения «до того». Если одно действие происходит до другого, то первое видно и упорядочено перед вторым.
Если у нас есть два действия x и y, мы пишем hb (x, y), чтобы указать, что x происходит раньшеy.
Если x и y являются действиями одного потока и x предшествует y в программном порядке, то hb (x, y). (1)
Если hb (x, y) и hb (y, z), то hb (x, z). (2)
Запись в энергозависимое поле происходит перед каждым последующим чтением этого поля. (3)
Действия, происходящие в одних и тех же потоках, упорядочены по (1). Таким образом, запись в y
происходит до записи в x
в потоке 1.
Запись в volatile x
в потоке 1 происходит до чтения обновленного значения x
в потоке 2, посколькув (3).
И чтение x
происходит - перед чтением y
в thread2, из-за (1).
Объединяя все это, следует, что если thread2 видитобновленное значение для x
, тогда произойдет чтение y
в этом потоке - перед записью в y
в другом потоке (и видимость обновленного значения гарантируется).