Семантика конечных полей, как определено в разделе 17.5 JLS , гарантирует, что:
Поток, который может видеть только ссылку на объект после этого объектаполностью инициализирован, гарантированно видит правильно инициализированные значения для конечных полей этого объекта.
Другими словами, он говорит, что , если поток видит полностью инициализированный объект, , затем гарантированно правильно будут видеть его окончательные поля.
Однако, нет никакой гарантии, что объект будет виден данному потоку.Это другая проблема.
Если вы не используете какую-либо синхронизацию для публикации ссылки на ваш объект, то другой поток может никогда не увидеть ссылку на него.
Рассмотрим следующий код:
final class A {
private final int x;
A(int x) { this.x = x; }
public getX() { return x; }
}
class Main {
static volatile A a1 = null;
static A a2 = null;
public static void main(String[] args) {
new Thread(new Runnable() { void run() { try {
while (a1 == null) Thread.sleep(50);
System.out.println(a1.getX()); } catch (Throwable t) {}
}}).start()
new Thread(new Runnable() { void run() { try {
while (a2 == null) Thread.sleep(50);
System.out.println(a2.getX()); } catch (Throwable t) {}
}}).start()
a1 = new A(1); a2 = new A(1);
}
}
Обратите внимание, что поле a1
является изменчивым.Это гарантирует, что в конце концов запись в это поле станет видимой для всех потоков, читающих ее через некоторое время.Поле a2
не является изменчивым (поэтому запись в это поле одним потоком может никогда не быть замечена другими потоками).
В этом коде мы можем быть уверены, что поток 1 завершит выполнение (чтото есть он увидит, что a1 != null
. Однако может случиться так, что поток 2 остановится, так как он никогда не увидит запись в поле a2
, так как он не является энергозависимым.