Если более одного потока могут получить доступ к полю, должно ли оно быть помечено как изменяемое? - PullRequest
7 голосов
/ 25 октября 2009

Чтение нескольких потоков ( распространенные проблемы параллелизма , изменчивое ключевое слово , модель памяти ) Я запутался в проблемах параллелизма в Java.

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

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

Для меня это относится только к JVM версии 1.5 и более поздним, но не ограничивайтесь ответом о моей конкретной настройке.

Ответы [ 4 ]

4 голосов
/ 25 октября 2009

Ну, вы прочитали эти другие вопросы, и я предполагаю, что вы уже прочитали ответы, поэтому я просто выделю некоторые ключевые моменты:

  1. они собираются измениться? если нет, вам не нужно летучих
  2. если да, то относится ли значение поля к другому? если да, переходите к пункту 4
  3. сколько потоков это изменит? если только 1, то volatile - это все, что вам нужно
  4. если ответом на число 2 является «нет» или в него будет записано более одного потока, тогда только volatile будет недостаточно , вам, вероятно, потребуется синхронизировать доступ

Добавлено:
Если поле ссылается на объект, то оно будет иметь собственные поля, и все эти соображения также применимы к этим полям.

3 голосов
/ 25 октября 2009

Если к полю получают доступ несколько потоков, оно должно быть volatile или final или доступно только для синхронизированных блоков. В противном случае назначенные значения могут быть не видны другим потокам.

Класс должен быть специально разработан для одновременного доступа несколькими потоками. Просто маркировки полей volatile или final недостаточно для безопасности потоков. Существуют проблемы согласованности (атомарность изменений нескольких полей), проблемы с межпотоковой сигнализацией (например, с использованием wait и notify) и т. Д.

Итак, безопаснее всего предположить, что объект должен быть виден только одному потоку, если не указано иное. Делать все ваши объекты поточно-ориентированными не нужно, и это дорого с точки зрения скорости программного обеспечения, но, что более важно, с точки зрения затрат на разработку.

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

2 голосов
/ 25 октября 2009

Если вам нужно спросить, используйте замки. volatile может быть полезно в некоторых случаях, но это очень, очень трудно понять правильно. Например:

class Foo {
  private volatile int counter = 0;
  int Increment() {
    counter++;
    return counter;
  }
}

Если два потока запускают Increment() одновременно, возможно, что результат будет counter = 1. Это связано с тем, что компьютер сначала извлечет counter, добавляет его, а затем сохраняет обратно. Volatile просто заставляет сохранять и загружать данные в определенном порядке относительно других операторов.

Обратите внимание, что synchronized обычно устраняет необходимость в volatile - если все обращения к данному полю защищены одним монитором, volatile никогда не потребуется.

Использование volatile для создания алгоритмов без блокировки очень, очень сложно; придерживайтесь synchronized, если у вас нет веских доказательств того, что это уже слишком медленно, и вы не провели детальный анализ алгоритма, который вы планируете реализовать.

1 голос
/ 25 октября 2009

Короткий ответ - нет. Проблемы с потоками требуют большего внимания и планирования, чем это. См. this для ознакомления с некоторыми ограничениями того, когда volatile помогает для потоков, а когда нет. Модификация значений должна быть должным образом синхронизирована, но, как правило, модификация требует состояния более чем одной переменной за раз. Например, у вас есть переменная, и вы хотите изменить ее, если она соответствует критериям. Чтение из массива и запись в массив - это разные инструкции, и их необходимо синхронизировать вместе. Летучих не достаточно.

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

...