2015-09-08 2 views
6

Я прочел несколько объяснений , раздел 16.3 «Инициализация безопасности» от JCIP и до сих пор не ясен. В разделе указывается, чтоДоступность видимости

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

Так что, если я имел следующий изменяемый объект:

public final class Container{ 
    private String name; 
    private int cupsWon; 
    private double netWorth; 

     public Container(String name, int cupsWon, double netWorth){ 
      this.name = name; 
      this.cupsWon = cupsWon; 
      this.netWorth = netWorth; 
     } 

    //NO Setters 
    //Getters 
} 

Затем Thread 1 создает его следующим образом и проходит с к thread2.

final Container c = new Container("Ted Dibiasi", 10, 1000000); 

Резьба2 (не одновременно, позволяет сказать, что через 1 мс), считывает значения с, возможно, что Резьба2 никогда не увидит

c.name=null or 
c.cupswon=0 or worst of all, 
c.netWorth=0.0? 

Приветствия

UPDATE

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

public final class Container{ 

    private String name; 
    private int cupsWon; 
    private double netWorth; 

    public Container(String name, int cupsWon, double netWorth){ 
     this.name = name; 
     this.cupsWon = cupsWon; 
     this.netWorth = netWorth; 
    } 

    public final String getName(){ 
     return name; 
    } 

    public final int getCupsWon(){ 
     return cupsWon; 
    } 

    public final double getNetWorth(){ 
     return netWorth; 
    } 

} 

// ----------

public final class Producer{ 

    private final Client client; 

    public Producer(Client client){ 
     this.client = client; 
    } 

    //Thread1 call produce() 
    public final void produce(){ 
     final Container c = new Container("Ted Dibiasi", 10, 1000000); 
     client.update(c); 
    } 

} 

// ----

public final class Client{ 

    private Container c; 
    //private volatile Container c;  

    public final void update(Container c){ 
      this.c = c; 
    } 

    //Thread2 calls consume(). 
    public final void consume(){ 
      String name = c.getName(); 
      int cupsWon = c.getCupsWon(); 
      double netWorth = c.getNetWorth();   
    } 

} 

Мои вопросы:

а) Когда Thread2 calls calls(), можно назвать, cupsWon, netWorth когда-либо равным null, 0 или 0.0? Я думал, что это CAN потому что поскольку поля в Container не являются окончательными, нет гарантии видимости.

б) Тем не менее, потом я прочитал раздел 16.3 и бит о «переменных, которые могут быть достигнуты с помощью конечного поля правильно построенного объекта», это означает, что, поскольку экземпляр контейнера С объявляется окончательным, есть DO есть осмотр на доступность в потреблении()?

final Контейнер c = новый контейнер («Ted Dibiasi», 10, 1000000);

c) Объявление ссылки на Container в классе Client как изменчивое решение не разрешает проблему видимости полей, поскольку она относится к ссылке.

+0

Поскольку строительство произошло до того, как оно было передано (как оно может быть принято иначе, в первую очередь?), Я предполагаю, что это невозможно. – MirMasej

+0

Можете ли вы прояснить, есть ли геттеры или нет? Я думаю, вы имеете в виду, что есть _ get_ getters, но _no_ сеттеры, тогда как есть ответ, который предполагает, что нет. – hiergiltdiestfu

+0

@hiergiltdiestfu Да, есть геттеры, но нет сеттеров. – CaptainHastings

ответ

6
final Container c = new Container("Ted Dibiasi", 10, 1000000); 

Если c здесь последнее поле в Thread1, а не локальная переменная, то цитата из Java Language Specification относится к этой конечной области c:

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

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

Хотя формулировка расплывчато здесь, я думаю, что «правильно инициализирован значение» и «уточненный в качестве конечного поля» означает, что если вы передаете c к Thread2 вне Thread1 конструктор, Thread2 всегда будет видеть полностью построенный экземпляр Container с инициализированными его полями.

+1

Но поля не являются окончательными! –

+0

Конечное поле в этом случае является «final Container c = new Container (« Ted Dibiasi », 10, 1000000);' –

+0

Итак, экземпляр контейнера - это объект, на который ссылается конечное поле 'Thread1'. И модель памяти гарантирует, что она по крайней мере актуальна как окончательное поле 'c', если конструктор' Thread1' закончен. –

0

Программистам обычно не нужно беспокоиться об этой проблеме. Это проблема только в том случае, если объект «опубликован небезопасно», например, объект привязан к энергонезависимому статическому полю Thread-1, а Thread-2 извлекает объект из чтения нелетучих данных. Однако это редко бывает; объекты передаются между потоками почти всегда с некоторым барьером памяти. Например, вам не нужно беспокоиться о видимости, когда вы передаете объект ThreadPoolExecutor.

Небезопасную публикацию следует избегать любой ценой, если она вам действительно не нужна, и вы точно знаете, что вы делаете.

Класс обычно не должен быть спроектирован так, чтобы выдерживать небезопасную публикацию, если нет веских оснований. Например, String спроектирован таким образом, потому что он широко используется в коде безопасности безопасности/контроля доступа, а содержимое строки должно быть постоянным, даже если некоторые враждебные программы пытаются саботировать его через небезопасную публикацию.

Для большинства классов не нужно использовать поле final для того, чтобы противостоять небезопасной публикации.

0

Чтобы ответить на ваш вопрос, нет, Thread2 никогда не увидит поля Контейнера в неинициализированном состоянии. Причина в том, что конструктор Container работает полностью до становится доступным ссылка Container c. Между вызовами может быть состояние гонки Client.update и Client.consume. В зависимости от результата этой гонки поле c является либо нулевым, либо полностью инициализированным объектом Контейнера во время вызова c.getName() в Client.consume. В первом случае вы получаете исключение NullPointerException, во втором случае - правильно инициализированное значение. Я не думаю, что это имеет какое-либо отношение к цитируемому предложению от JCIP.

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

  • Нет связанных вопросов^_^