2013-11-13 5 views
0

Недавно я запутался, когда я упомянул JMM о гарантиях, связанных с «окончательным». Вот выдержка и пример из JMMМодель памяти Java и конечные поля

На рисунке 4 приведен пример, показывающий, как конечные поля сравниваются с нормальными полями. Класс FinalFieldExample имеет конечное внутреннее поле x и неединичное int y. Один поток может выполнить метод writer(), а другой может выполнить метод reader(). Поскольку writer() записывает f после завершения работы конструктора объекта, читателю() будет гарантировано видеть правильно инициализированное значение для f.x: он будет читать значение 3. Однако f.y не является окончательным; метод для чтения(), следовательно, не гарантируется, чтобы увидеть значение 4 для него

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; // guaranteed to see 3 
     int j = f.y; // could see 0 
     } 
    } 
} 

Моей путаница в том, что является объектом «Obj» имеет конечные и неконечные поля полностью инициализирован и в настоящее время ссылаются на волоске 'T', T будет видеть только правильные значения для окончательных полей? Что относительно нефинальных полей, которые не изменяются после строительства. Я понимаю, что если они мутируются после того, как строительная нить «T» может не увидеть новое значение (если поле не изменчиво). Но я уверен, что если поле не является окончательным и не летучим и не будет изменено после строительства?

Как JVM реализует гарантии, связанные с «final»? Например, для летучих существуют барьеры памяти.

+0

Ответьте здесь: http://stackoverflow.com/questions/10301061/is-this-a-safe-publication-of-object/10301179#10301179 – Gray

+0

есть ошибка в этом коде - после того, как 'if (f! = Null)' check, 'f' может появиться снова null. код должен использовать локальную переменную для кэширования 'f' – ZhongYu

+0

http://stackoverflow.com/a/15342485/2031799 - вот как реализуются окончательные поля. Теоретически этот пример может сломаться, но не на практике. – Mikhail

ответ

1

Мое замешательство заключается в том, что объект «Obj» имеет окончательные и не конечные поля, полностью инициализированные и на которые ссылается поток «T», T будет видеть только правильные значения для конечных полей?

Все нити гарантированно видеть правильные значения для final полей. Это ничего не говорит о нефинализированных полях.

Как насчет полей, которые не изменяются после строительства.

Модификатор final относится только к определенному полю. Вы можете «посчастливиться» с другими нефинальными полями, но гарантия распространяется только на только по пометкам, указанным как final.

Тот факт, что нестрочное поле задано в конструкторе и не изменен позднее, не имеет значения.

+1

Практически это кажется достаточным для того, чтобы иметь хотя бы одно конечное поле в классе, чтобы сделать все остальные поля видимыми. Бар StoreStore синхронизировал все, и компилятор JIT не переупорядочивает код по границам конструктора. Конечно, не стоит полагаться на это поведение. – 30thh

4

Этот вопрос рассматривается в этом ответе:

Is this a safe publication of object?

Цитирую:

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

Если вы отметите поле как окончательное, оно заставляет компилятор завершить инициализацию для этого поля до завершения конструктора. Нефинансовые поля не имеют таких гарантий.

Это часть Java language definition (17.4). Подробная информация о final полей также находится в JLS (17.5).

Более конкретно, метод writer() создает экземпляр FinalFieldExample и хранит его в поле static для использования других потоков. Из-за переупорядочения команд поле y, возможно, еще не было инициализировано. Если же поток вызывает reader() он будет видеть y как 4 но другие потоки могли видеть его как 0, потому что f был установлен и потребляется, возможно, перед темy инициализируется и опубликовано.

Для того чтобы этот код был правильным, вы должны сделать f be volatile.

+0

Серый. Спасибо за ответ. Хотя инструкции по переупорядочению компилятора (инструкции ByteCode, а не инструкции программы) объясняют, почему f.y не вернется 4. JMM говорит: «Потому что writer() записывает f после того, как конструктор объекта завершает». Поэтому мне кажется, что спецификация исключает такое переупорядочение. – snegi

+0

Добавлено подробнее @snegi. Конструктор может закончить, поле 'f' задано, другие потоки могут читать' f.y', а _then_ он может быть инициализирован. Или инициализация, возможно, не была опубликована. – Gray

+0

Но если contructor закончен, поле f.y уже присвоено значение, правильно? – snegi

3

Как JVM реализует гарантии, связанные с «final»? Например, для летучих существуют барьеры памяти.

В честь семантики final полей, некоторые изменения порядка не может быть сделано, и некоторые барьеры памяти могут быть необходимы (на некоторых процессорах). см. http://g.oswego.edu/dl/jmm/cookbook.html

Это означает, что final не является бесплатным обедом. Учитывайте это, прежде чем мы будем использовать final везде. (Просто потому, что это в книге Джошуа не означает, что это правильно)

Смежные вопросы