Почему «Сохранение ссылки на него в конечном поле» и «правильно построенный объект» необходимы для безопасной публикации объекта? - PullRequest
1 голос
/ 12 апреля 2019

Я читаю "Параллелизм Java на практике", и он говорит:

Чтобы безопасно опубликовать объект, и ссылку на объект, и состояние объекта необходимо сделать видимыми для других потоковв то же время.Правильно сконструированный объект можно безопасно опубликовать:

  • Инициализация ссылки на объект из статического инициализатора;
  • Сохранение ссылки на него в энергозависимом поле или AtomicReference;
  • Сохранение ссылки на него в конечном поле правильно построенного объекта;или
  • Сохранение ссылки на него в поле, которое должным образом защищено замком.

Что я не понимаю, так это «Сохранение ссылки на него вfinal поле правильно сконструированного объекта ", Почему" правильно сконструированного объекта "необходимо? Без" правильно сконструированного объекта "другие потоки могут видеть публикуемый объектв несовместимом состоянии?

Я прочитал несколько связанных вопросов:

Но я не могу найти много объяснений о том, почему "правильно сконструированный объект" необходим.

Ниже приведен пример из ответа на вопрос «окончательная или нестабильная гарантия для безопасной публикации объектов - переполнение стека»

class SomeClass{
    private final SomeType field;

    SomeClass() {
        new Thread(new Runnable() {
            public void run() {
                SomeType copy = field; //copy could be null
                copy.doSomething(); //could throw NullPointerException
            }
        }).start();
        field = new SomeType();
    }
}

SomeClass isконечно, неправильно построенный copy может быть null, , но, по моему мнению, поток не может видеть copy в несовместимом состоянии, либо copy равно null, либо "и ссылка на copy иСостояние copy должно быть сделано видимым для потоков одновременно ".Таким образом, field публикуется безопасно, хотя SomeClass построено неправильно.Я прав?

Надеюсь, кто-нибудь может дать мне больше объяснений, заранее спасибо.

1 Ответ

0 голосов
/ 12 апреля 2019

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

однако обратите внимание, что final поля должны , а не изменять их значение.Если поток читает из final, он может смело предположить, что значение поля не изменится позже.Реализация потока или (JIT) -компилятор может «кэшировать» значение поля в некоторой переменной (или регистре), потому что final говорит ему, что значение, прочитанное один раз, остается неизменным.

В частности, код подобен

new Thread(new Runnable() {
    public void run() {
        while ( field == null ) {
          // Wait a little...
        }
        // Field was initialized, go ahead.
    }
}).start();

Вероятно, будет только два возможных результата: либо цикл никогда не будет введен, либо он станет бесконечным циклом.

Именно поэтому он особенно небезопасен получить доступ к final полю до его инициализации;и final поля гарантированно будут инициализированы только после завершения конструктора.

Проблема может стать более очевидной, если вы напишите полное имя поля в коде потока, который равен SomeClass.this.field.Вы можете опустить его, но компилятор неявно сгенерирует для вас правильный доступ.Используя полное имя поля, вы можете более четко видеть, что поток обращается к SomeClass.this.field до полной инициализации this.Таким образом, фактически вы не публикуете согласованный объект SomeType, но все еще несовместимый объект SomeClass, который содержит это поле.

...