Java: окончательное замораживание поля для объекта, достижимого из конечных полей - PullRequest
11 голосов
/ 15 декабря 2010

Рефкарта DZone под названием " Core Java Concurrency " сообщает:

После установки окончательные значения полей не могут быть изменены.Если пометить поле ссылки на объект как окончательное, это не препятствует изменению объектов, на которые есть ссылки из этого поля.Например, окончательное поле ArrayList нельзя изменить на другой ArrayList, но объекты могут быть добавлены или удалены в экземпляре списка.

и

Окончательное замораживание поля включаетне только конечные поля в объекте, но также и все объекты, достижимые из этих конечных полей.

Я не совсем уверен насчет второго утверждения.Означает ли это, что если у меня есть конечное поле в классе A типа Class B, которое, в свою очередь, имеет конечное поле типа Integer, то окончательное замораживание поля для экземпляра класса A завершается только после окончательного замораживания поля для b.cуже произошло?

public class A{

  public final B b = new B();

}

public class B{ 

  public final Integer c = 10;

}

Ответы [ 5 ]

11 голосов
/ 15 декабря 2010

Означает ли это, что если у меня есть последнее поле в классе A типа B, в котором, в свою очередь, есть последнее поле типа Integer, то окончательное замораживание поля для экземпляра класса A завершается только после последнегозамораживание поля для bc уже произошло?

Я думаю, я бы осторожно сказал, что окончательное замораживание поля в этом случае означает, что когда вы создаете экземпляр A и безопасно публикуете его,другие объекты никогда не увидят неинициализированное значение для b или c.

Я бы также сказал, что когда вы создаете экземпляр B внутри A, другой код инициализации внутри A никогда не увидит неинициализированное значение для c.

Один случай, когда я столкнулся с реальными вопросами относительно окончательного замораживания поля, это, например, класс, который содержит (изменяемый) HashMap, предназначенный только для чтения, инициализированный во время построения:

public class DaysOfWeek {
    private final Map daysOfWeek = new HashMap();
    public DaysOfWeek() { 
      // prepopulate my map
      daysOfWeek.put(0, "Sunday");
      daysOfWeek.put(1, "Monday");
      // etc
    }

    public String getDayName(int dayOfWeek) {
      return daysOfWeek(dayOfWeek);
    }
}

Здесь возникает вопрос: при условии, что этот объект безопасно опубликован, и, учитывая, что здесь нет синхронизации, безопасно ли для других потоков вызывать getDayName ()?Ответ - да, потому что окончательное замораживание поля гарантирует, что HashMap и все, что из него доступно (здесь это просто строки, но могут быть произвольно сложными объектами), будут заморожены в конце построения.[Если вы действительно хотите изменить эту карту после построения, вам понадобится явная синхронизация вокруг операций чтения и записи.] Вот блог длиной , исследующий тему, и просмотрите комментарии, чтобы найти интересные ответы от таких людей, как Брайан.Гетц.

Кстати, я автор рефкарты

6 голосов
/ 15 декабря 2010

Java-параллелизм на практике упоминает об этом в разделе 16.3:

Безопасность инициализации гарантирует, что для правильно построенных объектов, все темы будут видеть правильные значения конечных полей, которые были установлены конструктором, независимо от того, как объект опубликован. Далее любой переменные, которые могут быть достигнуты через последнее поле правильно построенный объект (такой как элементы окончательного массива или содержимое HashMap, на которое ссылается последнее поле) также гарантированно будет виден другим темам. Для объектов с окончательными полями, инициализация безопасность запрещает переупорядочивать любую деталь конструкции с начальной нагрузкой ссылки на этот объект. Все пишет в окончательные поля, сделанные конструктор, как и любой переменные достижимы через те поля, «замерзли», когда конструктор завершает и любой поток который получает ссылку на это объект гарантированно увидит значение это по крайней мере так же актуально, как замороженное значение. Пишет что инициализирует переменные достижимы через финал поля не упорядочены с операции после замораживание после строительства.

2 голосов
/ 15 декабря 2010

Гарантия сильнее, чем вы думаете.Конечная семантика поля применяется даже к изменяемым объектам, которые назначены конечным полям (с обычными ограничениями).ТАК расширив ваш пример, сделав A.b закрытым и B изменяемым (но не изменяемым извне).

public class A {
    private final B b = new B();
    public Integer get() { return b.c; }
}

public class B {
    public Integer c = 10;
}

В этом случае A.get никогда не вернет null даже при небезопасной публикации.Конечно, этот пример является абсолютно абстрактным и потому бессмысленным.Обычно это важно для массивов (например, в String) и коллекций.

2 голосов
/ 15 декабря 2010

правый. Это следует из JMM

Искать абзац:

Объект считается полностью инициализированным после завершения его конструктора. Поток, который может видеть ссылку на объект только после того, как этот объект был полностью инициализирован, гарантированно увидит правильно инициализированные значения для конечных полей этого объекта.

Поскольку конструктор не будет завершен до тех пор, пока класс B не инициализируется, что гарантирует замораживание B.c

1 голос
/ 15 декабря 2010

Не имеет никакого смысла говорить о том, что становится окончательным перед тем, что еще.Для вашей программы, как только ваш объект создан (фактически с момента, когда поле назначено один раз), ссылка больше не может измениться.Поскольку экземпляр B создается до экземпляра A, можно сказать, что c становится конечным до b, но это на самом деле не имеет значения.

Если порядок важен, когда у вас есть несколько полей final в одном классе.Если вы хотите использовать значение одного последнего поля в присваивании другого, вам следует обращаться только к тем полям, которые уже были инициализированы.

Если честно, то предложение «окончательное замораживание полей» не имеет особого смысладля меня.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...