Связь между Synchronized и Final в JAVA

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

Для. например приведенный ниже код:

class FinalFieldExample {
  final int x;
  int y;
  static FinalFieldExample f;
  public FinalFieldExample() {
    x = 3;
    y = 4;
  }

  static void writer() {
  f = new FinalFieldExample();
  }

  static void reader() {
    if (f != null) {
    int i = f.x;
    int j = f.y;
  }
 }  
}

Читатель может правильно прочитать значение x, но может прочитать значение y как 0, поскольку оно не объявлено окончательным.

Почему это происходит?


person ASingh    schedule 24.02.2013    source источник
comment
Как вы думаете, почему читатель может правильно прочитать значение x, но может прочитать значение y как 0, поскольку оно объявлено окончательным?   -  person zch    schedule 24.02.2013
comment
Покажите нам весь свой код, особенно метод main(), который показывает поведение, о котором вы заявляете, создавая таким образом SSCCE   -  person Bohemian♦    schedule 24.02.2013
comment
@Bohemian 2 потока, один вызывает писателя, а другой - читателя.   -  person assylias    schedule 24.02.2013
comment
@Bohemian: я имею в виду эту документацию: cs .umd.edu / ~ pugh / java / memoryModel / jsr-133-faq.html.   -  person ASingh    schedule 24.02.2013


Ответы (2)


Это связано с семантикой конечных полей. JLS № 17.5 дает хорошее резюме:

Модель использования полей final проста: установите поля final для объекта в конструкторе этого объекта; и не пишите ссылку на создаваемый объект в месте, где другой поток может видеть его до того, как конструктор объекта будет завершен. Если это будет выполнено, то, когда объект будет виден другим потоком, этот поток всегда будет видеть правильно сконструированную версию последних полей этого объекта.

Другими словами, при условии, что вы не позволите this уйти во время построения, у вас есть гарантия, что все потоки увидят правильное значение для x (т. Е. 3).

Что касается другого поля (y), при отсутствии синхронизации не дается никаких гарантий относительно того, какое значение будет отображаться (значение по умолчанию или значение конструктора).

person assylias    schedule 24.02.2013
comment
Я ценю ваше объяснение. Можете ли вы предоставить мне какой-нибудь пример инициализации этого типа конструктора ..? - person ASingh; 24.02.2013
comment
Не уверен, что понимаю ваш вопрос. - person assylias; 24.02.2013
comment
Вы хотите сказать, что в приведенном выше коде ... мы можем получить недопустимую запись для 'Y', потому что у меня есть статическая ссылка на объект класса, вне которого другой поток может вызвать ....? Я не понимаю, что именно здесь делает final ... Я понимаю, что final делает объект неизменным ... так что, если поток вызывает статическую ссылку еще до вызова конструктора ...? Тогда оба 'X 'и' Y 'не инициализируются. Итак, мой вопрос: как именно мы должны реализовать конструктор класса, чтобы никакой другой поток не мог его вызвать, пока он не будет должным образом инициализирован. - person ASingh; 24.02.2013
comment
Поточно-безопасная публикация объекта - более широкая тема, но в целом, помимо неизменяемых объектов, безопасная публикация требует некоторой формы синхронизации. - person assylias; 24.02.2013
comment
И да, поток может видеть, что f не равно нулю, но y по-прежнему равен своему начальному значению по умолчанию 0. - person assylias; 24.02.2013

final поля не имеют ничего общего с потоками. Я подозреваю, что вы думаете о volatile полях, которые действуют немного так, как вы описываете.

person OldCurmudgeon    schedule 24.02.2013
comment
Конечные поля имеют особое поведение в многопоточной среде, подобное изменчивым полям. - person assylias; 24.02.2013