Влияет ли изменчивость на летучие переменные?

Хорошо, предположим, что у меня есть куча переменных, один из которых объявлен volatile:

int a;
int b;
int c;
volatile int v;

Если один поток записывает все четыре переменные (записывается в v last), а другой поток читает из всех четырех переменных (сначала читает с v), второй листок видит значения, записанные в a, b и c по первому потоку, даже если они сами не объявлены изменчивыми? Или он может видеть устаревшие значения?

Поскольку, похоже, возникает некоторая путаница: я не намеренно пытаюсь сделать что-то небезопасное. Я просто хочу понять модель памяти Java и семантику ключевого слова volatile. Чисто любопытство.

Ответ 1

Да. volatile, блокировки и т.д., установите связь между событиями и ранее, но она влияет на все переменные (в новой модели памяти Java (JMM) из Java SE 5/JDK 1.4). Вид делает его полезным для не примитивных летучих веществ...

Ответ 2

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

Метод, который, по-видимому, вы пытаетесь использовать, включает использование одной изменчивой переменной в качестве защиты синхронизации в сочетании с одной или несколькими другими энергонезависимыми переменными. Этот метод применим, когда выполняются следующие условия:

  • Только один поток будет записывать в набор значений, предназначенных для защиты.
  • Потоки, считывающие набор значений, будут читать их только в том случае, если значение volatile guard соответствует некоторым критериям.

Вы не упомянете о втором условии, поддерживающем true для вашего примера, но мы все равно можем его изучить. Модель для писателя выглядит следующим образом:

  • Напишите все неизменяемые переменные, предполагая, что ни один другой поток не попытается их прочитать.
  • После завершения напишите значение переменной volatile guard, которая указывает, что критерии читателя выполнены.

Читатели работают следующим образом:

  • В любой момент прочитайте переменную volatile guard, и если ее значение соответствует критериям, то
  • Прочитайте другие энергонезависимые переменные.

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

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

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

Резервное копирование, трюк здесь - контролировать доступ к набору переменных без

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

Piggybacking поверх переменной volatile guard - это умный трюк, а не тот, который нужно делать небрежно. Последующие обновления программы могут нарушить вышеупомянутые хрупкие условия, устраняя гарантии согласованности, предоставляемые моделью памяти Java. Если вы решите использовать этот метод, четко сформулируйте его инварианты и требования в коде.

Ответ 3

выполняет ли этот второй поток значения, записанные в a, b и c первым потоком, даже если они сами не объявлены изменчивыми? Или он может видеть устаревшие значения?

Вы получите устаревшие чтения, b/c, вы не можете гарантировать, что значения a, b, c являются значениями, установленными после чтения v. Использование конечного автомата (но вам нужно CAS для изменения состояния) является способ решения аналогичных вопросов, но это выходит за рамки обсуждения.

Возможно, эта часть неясна, после записи в v и чтения сначала с v вы получите правильные результаты (нестатические чтения), основная проблема заключается в том, что если вы это сделаете if (v==STATE1){...proceed...}, нет гарантии, что какой-либо другой поток не будет изменять состояние a/b/c. В этом случае будут прочитаны состояния. Если вы измените a/b/c + v один раз, вы получите правильный результат.

Освоение concurrency и без блокировок структур очень тяжелое. У Дуга Ли есть хорошая книга, и большинство разговоров/статей доктора Клиффа Клика - замечательное богатство, если вам нужно что-то начать копать.

Ответ 4

Да, волатильная запись "произойдет-до" следующего изменчивого чтения по той же переменной.

Хотя @seh правильно относится к проблемам согласованности с несколькими переменными, существуют случаи, когда требуется меньшая согласованность.

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

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