Я читаю книгу Брайана Гетца о параллелизме в Java на практике. Пункты 3.5 и 3.5.1 содержат утверждения, которые я не могу понять.
Рассмотрим следующий код:
public class Holder {
private int value;
public Holder(int value) {
this.value = value;
}
public void assertValue() {
if (value != value) throw new AssertionError("Magic");
}
}
class HolderContainer {
// Unsafe publication
public Holder holder;
public void init() {
holder = new Holder(42);
}
}
Автор утверждает, что:
- В Java конструктор объекта сначала записывает значения по умолчанию во все поля перед запуском конструктора подкласса.
- Следовательно, можно увидеть значение поля по умолчанию как устаревшее значение.
- Поток может увидеть устаревшее значение при первом чтении поля, а затем более актуальное значение в следующий раз, поэтому assertN может выдать AssertionError.
Итак, согласно тексту, с некоторыми неудачными временами возможно, что value = 0; и в следующий момент value = 42.
Я согласен с пунктом 1, что конструктор объекта сначала заполняет поля значениями по умолчанию. Но я не понимаю пунктов 2 и 3.
Давайте обновим код авторов и рассмотрим следующий пример:
public class Holder {
int value;
public Holder(int value) {
//Sleep to prevent constructor to finish too early
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.value = value;
}
public void assertValue() {
if(value != value) System.out.println("Magic");
}
}
Я добавил Thread.sleep (3000), чтобы заставить поток ждать, прежде чем объект будет полностью построен.
public class Tests {
private HolderContainer hc = new HolderContainer();
class Initialization implements Runnable {
public void run() {
hc.init();
}
}
class Checking implements Runnable {
public void run() {
hc.holder.assertValue();
}
}
public void run() {
new Thread(new Initialization()).start();
new Thread(new Checking()).start();
}
}
Например:
- первый поток в своем объекте-держателе
- второй поток вызывает assertValue
Основной поток запускает два потока:
- новый поток (новая инициализация ()). start (); На создание объекта-держателя уходит 3 секунды.
- новый поток (новая проверка ()). start (); поскольку объект-держатель все еще не создан, код будет генерировать исключение NullPointerException
Следовательно, невозможно эмулировать ситуацию, когда поле имеет значение по умолчанию.
Мои вопросы:
- Автор ошибался насчет этой проблемы параллелизма?
- Или невозможно эмулировать поведение для значений полей по умолчанию?
Tests
класс из книги? (извините, пропустил эти два пункта) - person AxelH   schedule 02.06.2018if (value != 42)
. - person Boaz   schedule 01.09.2018value != value
так, чтобы она всегда была ложной. Тем не менее автор прав. Многие ошибки параллелизма появляются только тогда, когда вам не повезло. Кроме того, @Boaz неверен, когда говорит, что проблема возможна только в том случае, если поток просматривается в конструкторе. Другие потоки могут не видеть записи в том же порядке, в котором они были сделаны. - person Matt Timmermans   schedule 27.07.2020