2012-02-25 4 views

ответ

1

новому оператору разрешено возвращать значение до завершения конструктора класса. Таким образом, переменная может не читать значение null, но содержит неинициализированный экземпляр класса. Это происходит из-за переупорядочения байтов.

Некоторое разъяснение: С точки зрения одного потока JVM разрешено изменять порядок инструкций. При создании экземпляра традиционно вы могли бы подумать, что идет как это:

  • выделить память
  • инициализации
  • запуска (конструктор)
  • ссылка Присвоить вар

Хотя на самом деле виртуальная машина может сделать что-то вроде:

  • выделить память
  • правопреемник ссылка на вар
  • инициализация запуска (конструктор)

Это имеет преимущество в производительности, так как адреса не должны быть LookUp снова. С точки зрения одного потока это не меняет порядок логики. Программа отлично работает. Но это создает проблему в многопоточном коде. Это означает, что ссылка может быть опубликована до запуска конструктора. Для этого вам необходимо выполнить правило «бывает раньше», чтобы убедиться, что экземпляр полностью инициализирован. Объявление переменных volatile dos обеспечивает соблюдение таких правил - до правил.

Подробнее о переназначения: http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#reordering

+0

Не могли бы вы привести источник? – musiKk

+0

@musiKk хороший вопрос, ищется. –

+2

Я бы подумал, что вместо этого ссылка скорее всего будет говорить о публикации объекта внутри конструктора (или того, что он вызывает). Я не могу придумать случай, когда новый оператор возвращает ссылку на объект перед тем, как вызванный конструктор будет завершен. – arcy

3

Рассмотрим этот код:

public class World{ 
    public static Point _point; 

    public static void main(String[] args){ 
     new PointMaker().start(); 
     System.out.println(_point); 
    } 
} 

public class Point{ 
    private final int _x, _y; 

    public Point(int x, int y){ 
     _x = x; 
     World._point = this;//BAD: publish myself before I'm fully constructed 
     //some long computation here 
     _y = y; 
    } 

    public void toString(){ 
     return _x + "," + _y; 
    } 
} 

public class PointMaker extends Thread{ 
    public void run(){ 
     new Point(1, 1); 
    } 
} 

Поскольку Point публикует себя перед установкой значения _y, вызов println может уступить "1,0" вместо ожидаемого "1,1".

(Обратите внимание, что он также может дать "null" если PointMaker + Point.<init> не получают достаточно далеко, чтобы установить World._point поле перед вызовом println Исполняет.)

+1

Чтобы быть точным, он может даже напечатать «0,0». Если мы пропустим эту ссылку от конструктора, JLS не сделает никаких гарантий в финале. – Voo

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