Одновременное чтение / запись в переменную в Java - PullRequest
9 голосов
/ 01 августа 2011

Если у меня есть переменная, из которой читают несколько потоков и пишет только один поток, нужно ли иметь блокировку вокруг этой переменной?Произойдет ли сбой, если один поток попытается прочитать, а другой поток попытается записать одновременно?

Ответы [ 3 ]

8 голосов
/ 01 августа 2011

Проблема параллелизма заключается не в сбое, а в том, какую версию данных вы видите.

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

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

Запись в некоторые типыполя являются атомарными, но без отношения происходит до , что обеспечивает правильное упорядочение памяти (если вы не используете volatile);см. эту страницу для деталей.

4 голосов
/ 02 августа 2011

Простой ответ - да, вам нужна синхронизация.

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

Когда я говорю «некоторая форма синхронизации», я имею в виду нечто более точное, что создает отношение «происходит до» (так называемый барьер памяти) между точками записи и чтения.Классы синхронизации или java.util.concurrent.lock являются наиболее очевидным способом создания такой вещи, но все параллельные коллекции обычно также предоставляют аналогичные гарантии (проверьте javadoc, чтобы быть уверенным).Например, выполнение параллельных очередей «положить» и «взять» создаст отношение «до того, как произойдет».

Пометка поля как изменчивого не дает вам видеть непоследовательные ссылки (длинные разрывы) и гарантирует, что все потоки «увидят»"написать.Но изменяемые поля записи / чтения не могут быть объединены с другими операциями в больших элементарных единицах.Классы Atomic обрабатывают общие комбинированные операции, такие как сравнение и установка или чтение и увеличение.Синхронизация или другие java.util.concurrent синхронизаторы (CyclicBarrier и т. Д.) Или блокировки должны использоваться для больших областей исключительности.

Отходя от простого да, есть случаи, которые больше "нет", если вы действительно знаетечто ты делаешь".Два примера:

1) Особый случай поля, которое является окончательным и записывается ТОЛЬКО во время построения.Одним из примеров этого является заполнение предварительно вычисленного кеша (например, карта, где ключи - это общеизвестные значения, а значения - предварительно вычисленные производные значения).Если вы строите это в поле перед созданием, и поле является окончательным, и вы никогда не пишете в него позже, конец конструктора выполняет «окончательное замораживание поля» и последующие операции чтения НЕ требуют синхронизации.

2) Случай шаблона "racy single check", который рассматривается в Effective Java.Канонический пример находится в java.lang.String.hashCode ().Строка имеет поле хеша, которое лениво вычисляется при первом вызове hashCode () и кэшируется в локальное поле, которое НЕ синхронизируется.По сути, несколько потоков могут стремиться вычислить это значение и установить его для других потоков, но потому что оно защищено хорошо известным sentinel (0) и всегда вычисляет одинаковое значение (поэтому нам не важно, какой поток "выиграет" илинесколько делать), это на самом деле гарантированно будет в порядке.

Более длинная ссылка (написано мной): http://refcardz.dzone.com/refcardz/core-java-concurrency

2 голосов
/ 01 августа 2011

Помните, что volatile НЕ является атомарным, что означает, что double и long, использующие 64 бита, могут быть прочитаны в несовместимом состоянии, где 32 бита являются старым значением, а 32 бита новым. Кроме того, изменяемые массивы не делают записи массива изменчивыми. Настоятельно рекомендуется использовать классы из java.util.concurrent.

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