Параллелизм Java: является ли конечное поле (инициализированное в конструкторе) поточно-ориентированным? - PullRequest
17 голосов
/ 23 июня 2011

Может кто-нибудь сказать мне, является ли этот класс потокобезопасным или нет?

class Foo {

    private final Map<String,String> aMap;

    public Foo() {
        aMap = new HashMap<String, String>();
        aMap.put("1", "a");
        aMap.put("2", "b");
        aMap.put("3", "c");
    }

    public String get(String key) {
        return aMap.get(key);
    }

}

Редактировать: Я виноват, что не уточнил вопрос. Согласно JMM FAQ :

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

* * 1010

Это заставило меня запутаться, что для aMap установлено значение aMap = new HashMap<String, String>();. Таким образом, другие темы могут видеть эти

aMap.put("1", "a");
aMap.put("2", "b");
aMap.put("3", "c");

или нет?

Редактировать: Я нашел этот вопрос , который точно закрыт для моего вопроса

Ответы [ 7 ]

19 голосов
/ 23 июня 2011

Как уже указывалось, он абсолютно поточнобезопасен, и final здесь важен благодаря эффектам видимости памяти.

Наличие final гарантирует, что другие потоки увидят значения на карте после завершения конструктора без какой-либо внешней синхронизации. Без final это не может быть гарантировано во всех случаях, и вам нужно будет использовать идиомы безопасной публикации при создании нового объекта, доступного для других потоков, а именно (из Java Concurrency in Practice ):

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

Да, это так.Невозможно изменить саму ссылку aMap или добавить ее на карту после конструктора (без отражения).

Если вы выставите aMap, этого не произойдет, поскольку два потока могут затем изменитькарта в то же время.

Вы можете улучшить свой класс, сделав aMap неизменяемым через Collections.unmodifiableCollection или Collections.unmodifiableMap .

2 голосов
/ 23 июня 2011

Гуава имеет неизменяемые классы для упрощения и гарантирования неизменности таких вещей:

private final ImmutableMap<String, String> aMap = ImmutableMap.of(
    "1", "a",
    "2", "b",
    "3", "c");
1 голос
/ 23 июня 2011

В этом классе нет проблем с параллелизмом, поскольку вы предоставляете только метод get.Если вы добавляете какой-либо метод, который изменяет карту, вы должны пометить этот метод как synchronized.

1 голос
/ 23 июня 2011

Да, при условии, что это полное определение класса, а не его фрагмент .

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

0 голосов
/ 25 апреля 2016

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

aMap = new HashMap<String, String>();

В соответствии с примером, приведенным в http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html,

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; // x is guaranteed to be 3
       int j = f.y; // y can have any value 
     }
   }
}

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

aMap.put("1", "a");
aMap.put("2", "b");
aMap.put("3", "c");

РЕДАКТИРОВАТЬ Мои плохие видели комментарии ниже кода позже

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

0 голосов
/ 23 июня 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...