Во-первых, позвольте мне сказать, что я знаю, что это довольно распространенная тема здесь, но, ища ее, я не смог найти другой вопрос, который проясняет следующую ситуацию. Мне очень жаль, если это возможный дубликат, но вот:
Я новичок в параллелизме, и мне дали следующий код, чтобы ответить на вопросы:
- а) Почему возможен любой другой вывод, кроме 00?
- б) Как изменить код, чтобы 00 печаталось ВСЕГДА.
boolean flag = false;
void changeVal(int val) {
if(this.flag){
return;
}
this.initialInt = val;
this.flag = true;
}
int initialInt = 1;
class MyThread extends Thread {
public void run(){
changeVal(0);
System.out.print(initialInt);
}
}
void execute() throws Exception{
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.start(); t2.start(); t1.join(); t2.join();
System.out.println();
}
Для a) мой ответ будет следующим: в отсутствие какой-либо конструкции volatile/sync компилятор может изменить порядок некоторых инструкций. В частности, this.initialInt = val; и this.flag = true; может быть переключен так, чтобы могла возникнуть следующая ситуация: оба потока запущены, и t1 загружается вперед. Учитывая переупорядоченные инструкции, он сначала устанавливает флаг = true. Теперь, прежде чем он достигнет теперь последнего оператора this.initialInt = val; другой поток прыгает, проверяет условие if и немедленно возвращает, таким образом печатая неизменное значение initialInt, равное 1. Кроме того, я считаю, что без какой-либо volatile/синхронизации неясно, может ли t2 увидеть назначение, выполненное для initialInt в t1 поэтому он также может печатать 1 как значение по умолчанию.
Для b) я думаю, что этот флаг можно сделать изменчивым. Я узнал, что когда t1 записывает в изменчивую переменную, устанавливающую флаг = true, тогда t2, после считывания этой изменчивой переменной в операторе if, увидит любые операции записи, выполненные до изменчивой записи, следовательно, initialInt = val тоже. Следовательно, t2 уже увидит, как его значение initialInt изменилось на 0, и всегда должен печатать 0. Однако это будет работать только в том случае, если использование volatile успешно предотвращает любое изменение порядка, как я описал в а). Я читал о том, что volatile выполняет такие вещи, но я не уверен, всегда ли это работает здесь при отсутствии каких-либо дополнительных синхронизированных блоков или каких-либо подобных блокировок. Из этот ответ я понял, что ничего, что происходит до энергозависимого хранилища (так что this.flag = true), не может быть переупорядочено так, чтобы оно отображалось за пределами Это. В этом случае initialInt = val не может быть перемещен вниз, и я должен быть прав, верно? Или не ? :)
Большое вам спасибо за вашу помощь. Я с нетерпением жду ваших ответов.