2016-10-23 9 views
3

В этом учебнике (link) на летучей декларации Java заключается в следующем примере:разовых безопасная публикация через Java летучего

public class BackgroundFloobleLoader { 
    public volatile Flooble theFlooble; 

    public void initInBackground() { 
     // do lots of stuff 
     theFlooble = new Flooble(); // this is the only write to theFlooble 
    } 
} 

public class SomeOtherClass { 
    public void doWork() { 
     while (true) { 
      // do some stuff... 
      // use the Flooble, but only if it is ready 
      if (floobleLoader.theFlooble != null) 
       doSomething(floobleLoader.theFlooble); 
     } 
    } 
} 

Говорит, что фоновый поток загружается из базы данных, так что я думаю, что автор означает, что экземпляр new Flooble() занимает значительное количество времени.

Цитирую: Without the theFlooble reference being volatile, the code in doWork() would be at risk for seeing a partially constructed Flooble as it dereferences the theFlooble reference.

Как это может быть? Я бы ожидал противоположного. То есть, я ожидал, что без волатильной декларации поток, вызывающий метод doWork, рискует увидеть Флообл с опозданием или вообще никогда.

+0

Вы никогда не сможете увидеть его, увидеть его поздно и/или увидеть его в несогласованном состоянии. – assylias

+0

Кажется, что экземпляр должен быть полным (даже если чтение базы данных занимает много времени) до назначения ссылки. В этом случае я не понимаю, почему ссылка на объект будет находиться в противоречивом состоянии. – H2ONaCl

+0

Именно ваше ожидание - разумное ожидание, но, к сожалению, если бы это было так, то Java не смогла бы масштабировать до большого количества одновременных процессоров. –

ответ

3

Это может быть вызвано переупорядочиванием компилятора. Компилятор может встроить конструктор Flooble и изменить порядок инициализации его полей и присвоить ссылку на переменную theFlooble. Объявление theFlooble volatile предотвращает такие переупорядочивания.

См https://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

+0

Я удивлен. Такое переупорядочение почти похоже на саботаж или, по крайней мере, оптимистическую ошибку. – H2ONaCl

+0

@ H2ONaCl он может чувствовать себя так, как сначала. Но он начинает иметь большой смысл при исследовании истории аппаратного дизайна. Когда-то процессоры могли быть быстрее, просто увеличив тактовую частоту. Эта «легкая победа» больше невозможна, и для получения дополнительных преимуществ от процессоров аппаратные парни должны проходить все параллельно и притягивать некоторые довольно умные трюки. Java делает удивительную работу по изоляции наших программистов от этих оптимизаций ... большую часть времени. –

+0

@ H2ONaCl Мартин Томпсон много обсуждает в своем блоге, который отлично читается. Если у вас есть время, я рекомендую выезд http://mechanical-sympathy.blogspot.co.uk. http://mechanical-sympathy.blogspot.co.uk/2013/02/cpu-cache-flushing-fallacy.html - хорошее начало для повышения осведомленности о различных проблемах. –

1

компилятор может встраивать конструктор Flooble и изменить порядок инициализации его полей и присвоения ссылки на theFlooble переменную

Я видел это заявил в блогах, SO сообщений и множество других мест, но это прямо противоречит спецификации языка java:

https://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.5

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

Одна сборка декомпиляции, которую я видел, похоже, показала, что это было сгенерировано древним компилятором Symantec JIT - не совсем официальным.

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

+0

Я хотел бы получить хороший ответ на этот вопрос. Мое понимание согласуется с этим ответом, но также и то, что учебник написан Брайаном Гетцем, который буквально написал книгу о параллелизме Java. –