Можно ли изменить порядок операций, которые последовательно присваивают значения нескольким изменчивым полям?

Всегда ли следующий код будет распечатывать «правильное» значение данных? Даже если есть другие операции между присвоением значений data и dataReady в методе setData?

Или же JVM могла бы изменить порядок этих операций так, чтобы RunningThread мог видеть dataReady как истинное, в то время как данные все еще были пустыми?

Если код безопасный и правильный - что такого в изменчивом поле, которое предотвращает проблемы с переупорядочением? Означает ли это, что сами данные даже не обязательно должны быть изменчивыми, что есть что-то в том, чтобы сделать только dataReady изменчивым, что заставляет JVM не кэшировать локальные значения для других полей экземпляра, в котором одно поле является изменчивым?

public class RunningThread implements Runnable {

private volatile boolean dataReady = false;
private volatile String data = null;

public void run() {
    while (!dataReady){
        //Do stuff or wait
    }
    System.out.println("Value of data '" +data +"' is definitely not null.");
}


//setData called by another thread
public void setData(String dataToSet){
    if (dataToSet!=null){
        data = dataToSet;
        dataReady = true;
    }
}

}

person RonR    schedule 19.05.2015    source источник
comment
О да! Спасибо @Holger. Исправлено.   -  person RonR    schedule 19.05.2015


Ответы (1)


Все записи, выполненные потоком перед записью в переменную volatile, будут видны другому потоку после чтения значения из переменной volatile, записанного предыдущим потоком.

Спецификация языка Java такова:

Если x и y являются действиями одного и того же потока и x стоит перед y в программном порядке, то hb (x, y).

Если hb (x, y) и hb (y, z), то hb (x, z).

Запись в поле volatile (§8.3.1.4) происходит до каждого последующего чтения этого поля.

Таким образом, запись в data происходит-до, тот же поток записывает в dataReady, что происходит-до, второй поток читает dataReady, что происходит-до второго поток читает data.

Другими словами, гарантии видимости памяти в отношении переменной dataReady достаточно для защиты от чтения data, даже не нужно объявлять data как volatile.

person Holger    schedule 19.05.2015
comment
Разве data не будет потокобезопасным, даже если не пометить его как volatile и не использовать флаг dataReady? (поскольку String неизменяем) - person CKing; 19.05.2015
comment
@Chetan Kinger: ну, если data volatile и читатель проверяет null, вам не понадобится другой флаг, но я полагаю, что это просто упрощенный пример. Если data не volatile, вы все равно никогда не увидите поврежденные значения, поскольку String неизменяем, но без флага volatile у вас нет гарантий увидеть самые последние значения, поэтому поток опроса может закончить бесконечный цикл, всегда видя null. - person Holger; 19.05.2015