На эту тему есть неплохая статья на https://habr.com/ru/post/133981/ либо можно скачать полную спецификацию работы JMM(java memory model) https://download.oracle.com/otndocs/jcp/memory_model-1.0-pfd-spec-oth-JSpec/
Если вкратце, reodering - это то в каком порядке выполняются операции и доступ потоков, а набор правил описаны в соотношении happens before.
public class Data {
int maxAllowedValue;
public Data() {
this.maxAllowedValue = 9000;
}
}
public class Keeper {
private Data data = null;
public Data getData() {
if(data == null) {
synchronized(this) {
if(data == null) {
data = new Data();
}
}
}
return data;
}
}
Поток, который первый обнаружит, что data == null
, выполнит следующие действия:
- Выделит память под новый объект
- Вызовет конструктор класса
Data
- Запишет значение 9000 в поле
maxAllowedValue
класса Data
- Запишет только что созданный объект в поле
data
класса Keeper
Другой поток может увидеть произошедшее в пункте 4 до того, как он увидит произошедшее в пунктах 3, т.е не в том порядке в которым логично ожидать, поэтому был создан набор правил, по которым оптимизатору/компилятору запрещено выполнять reordering. Набор правил описан через соотношение happens before:
1.Освобождение (releasing) монитора happens-before заполучение (acquiring) того же самого монитора
2.Запись в volatile переменную happens-before чтение из той же самой переменной
3. Запись значения в final
-поле при конструировании объекта happens-before запись этого объекта в какую-либо переменную, происходящая вне этого конструктора.