Рассмотрим следующий фрагмент кода
class Person {
private final String firstName;
private final String lastName;
Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
String getFirstName() {
return this.firstName;
}
String getLastName() {
return this.lastName;
}
}
class PersonPrinter {
private Person person;
void print() {
Person p = this.person;
System.err.println(p.getFirstName());
System.err.println(p.getLastName());
}
void setPeron(Person person) {
this.person = person;
}
}
Представьте, что есть одновременные вызовы методов setPerson()
и print
несколькими потоками в одном экземпляре PersonPrinter
.
Потому что код не синхронизирован должным образом, JVM может вносить оптимизацию, пока сохраняется семантика « as if serial ».
В частности, метод print()
может переупорядочиваться как:
void print() {
System.err.println(this.person.getFirstName());
System.err.println(this.person.getLastName());
}
Это удаление локальной переменной в пользу прямого доступа к полю: это не изменит семантику одного потока. Конечно, такая оптимизация может привести к распечатке имени одного человека и фамилии другого (поскольку другой параллельный поток может вызывать setPerson()
).
У меня такое ощущение, что отметка поля person
as volatile в PersonPrinter решит проблему и предотвратит удаление локального (следовательно, гарантируя, что мы печатаем firstName и lastName одного и того же человека) .... но я не смог найти причину, почему это так в oracle docs: если посмотреть на правила модели памяти java, что мешает удалить локальных пользователей в пользу прямого доступа к изменчивому полю?