И у нас есть два треда, которые выполняют следующие действия: Thread 1
a = 1;
flag = true;
Thread 2
if (flag)
System.out.println(a);
Фишка в том, что благодаря volatile переменной второй либо ничего не отпечатает, либо гарантированно отпечатает 1. Это аксиома, именно для этого семантика volatile правилась в обновленной JMM (http://www.ibm.com/developerworks/library/j-jtp02244/index.html).
Но давайте теперь рассмотрим несколько иной случай.
int a = 0;
boolean flag = false;
volatile int var1 = 0;
volatile int var2 = 0;
Thread 1
a = 1;
flag = true;
var1 = 1; // Записали в volatile
Thread 2
int tmp = var2; // Прочитали другую volatileif (flag)
System.out.println(a);
Вопрос: каковы возможные результаты работы Thread 2 (можно выбрать несколько)?
а) Ничего не отпечатает;
б) Отпечатает 0;
в) Отпечатает 1.
Re: Вопрос на засыпку: синхронизация на разных volatile
Здравствуйте, seno, Вы писали:
S>Вопрос: каковы возможные результаты работы Thread 2 (можно выбрать несколько)? S>а) Ничего не отпечатает; S>б) Отпечатает 0; S>в) Отпечатает 1.
абв
Re: Вопрос на засыпку: синхронизация на разных volatile
Здравствуйте, seno, Вы писали:
S>Вопрос: каковы возможные результаты работы Thread 2 (можно выбрать несколько)? S>а) Ничего не отпечатает; S>б) Отпечатает 0; S>в) Отпечатает 1.
Все варианты возможны. В данном сценарии нет никаких ограничений.
Если даже считывать ту же переменную, то все равно все будет так же, т.к. нет гарантии, что второй поток выполнится до присваивания volatile в первом.
Еще момент, что компилятор в праве переупорядочить присваивания 'a' и 'flag' или придумать еще более страшную оптимизацию, так как они не volatile.
Re: Вопрос на засыпку: синхронизация на разных volatile
Здравствуйте, StanislavK, Вы писали:
SK>Все варианты возможны. В данном сценарии нет никаких ограничений. SK>Если даже считывать ту же переменную, то все равно все будет так же, т.к. нет гарантии, что второй поток выполнится до присваивания volatile в первом. SK>Еще момент, что компилятор в праве переупорядочить присваивания 'a' и 'flag' или придумать еще более страшную оптимизацию, так как они не volatile.
В том то и дело, что есть ограничения. Семантика volatile такова, что его нельзя реордерить, и его чтение/запись выступают в роли "полупроницаемых" барьеров. Поэтому для кода:
a = 1;
b = 1;
volatile = true;
... возможны только два варианта выполнения:
a = 1;
b = 1;
volatile = true;
или
b = 1;
a = 1;
volatile = true;
Третьего не дано, запись в a и b гарантированно будет произведена до записи в volatile. Но для кода:
a = 1;
b = 1;
volatile = true;
c = 1;
... запись в с может быть произведена до записи в volatile (полупроницаемость).
Для volatile read все с точностью наоборот: код может пролезть "сверху", но не "снизу".
Re[2]: Вопрос на засыпку: синхронизация на разных volatile
Under the old memory model, accesses to volatile variables could not be reordered with each other, but they could be reordered with nonvolatile variable accesses. This undermined the usefulness of volatile fields as a means of signaling conditions from one thread to another.
Under the new memory model, it is still true that volatile variables cannot be reordered with each other. The difference is that it is now no longer so easy to reorder normal field accesses around them. Writing to a volatile field has the same memory effect as a monitor release, and reading from a volatile field has the same memory effect as a monitor acquire. In effect, because the new memory model places stricter constraints on reordering of volatile field accesses with other field accesses, volatile or not, anything that was visible to thread A when it writes to volatile field f becomes visible to thread B when it reads f.
Re[3]: Вопрос на засыпку: синхронизация на разных volatile
Здравствуйте, seno, Вы писали:
SK>Все варианты возможны. В данном сценарии нет никаких ограничений. SK>>Если даже считывать ту же переменную, то все равно все будет так же, т.к. нет гарантии, что второй поток выполнится до присваивания volatile в первом. SK>>Еще момент, что компилятор в праве переупорядочить присваивания 'a' и 'flag' или придумать еще более страшную оптимизацию, так как они не volatile.
S>В том то и дело, что есть ограничения. Семантика volatile такова, что его нельзя реордерить, и его чтение/запись выступают в роли "полупроницаемых" барьеров. Поэтому для кода: S>
S>a = 1;
S>b = 1;
S>volatile = true;
S>
S>... возможны только два варианта выполнения: S>
S>a = 1;
S>b = 1;
S>volatile = true;
S>
S>или S>
S>b = 1;
S>a = 1;
S>volatile = true;
S>
S>Третьего не дано, запись в a и b гарантированно будет произведена до записи в volatile. Но для кода: S>
S>a = 1;
S>b = 1;
S>volatile = true;
S>c = 1;
S>
S>... запись в с может быть произведена до записи в volatile (полупроницаемость). S>Для volatile read все с точностью наоборот: код может пролезть "сверху", но не "снизу".
Я и не спорил так все и сказал. Разве нет? В последнем примере, присваивания 'a' и 'flag' можно менять местами,
Насчет ограничений, они не имеют знаничея для примера, т.к. никак на него не влияют. Из-за того, что в ващем примере нет условия с использованием volatile переменной, то абсолютно все равно были присваивания с ней переупорядочены или нет. Так, что логично сказать, что ограничений нет. Но это уже вопрос логики, а не volatile
Re[4]: Вопрос на засыпку: синхронизация на разных volatile
Да, ваша правда. То есть, если
а) в Thread1 произошел реордеринг а и flag
б) Тhread2 таки увидел изменение flag до того, как Thread1 произвел запись в volatile
... то все, приехали
Re[3]: Вопрос на засыпку: синхронизация на разных volatile
S>В том то и дело, что есть ограничения. Семантика volatile такова, что его нельзя реордерить, и его чтение/запись выступают в роли "полупроницаемых" барьеров. Поэтому для кода:
Забудьте про "нельзя реордерить". В Memory model реордеринги идут как "опциональное" объяснение и в формальной модели нигде не используются. В формальной модели используются happens-before relationship, для которого важно читать одну и ту же переменную. Запись/чтение разных volatile такую связь не создают, поэтому там можно увидеть все, что угодно. И связано это может быть не с реордерингами, а с кэшами.
Вообще, для объяснения почти всей JMM (а может и всей) не нужны никакие переупорядочивания. Нужна "виртуальная NUMA-машина". Читать здесь пост от Maxim Karvonen (во второй половине поста). На практике (в реальных машинах) в некорректно-синхронизированных программах могут далеко не все эффекты наблюдаться (даже допускаемые NUMA-машиной), но это уже от конкретной JVM и архитекруты зависит.
Re[4]: Вопрос на засыпку: синхронизация на разных volatile
Здравствуйте, maxkar, Вы писали:
M>Забудьте про "нельзя реордерить". В Memory model реордеринги идут как "опциональное" объяснение и в формальной модели нигде не используются. В формальной модели используются happens-before relationship, для которого важно читать одну и ту же переменную. Запись/чтение разных volatile такую связь не создают, поэтому там можно увидеть все, что угодно. И связано это может быть не с реордерингами, а с кэшами.
В том то и дело, что создают! Вспомните, как релизованы мембары на уровни ОС? Разве там есть, условно, membar(variable)? Нет, там есть просто membar(). Он не ассоциирован ни с какой переменной или объектом. Это просто барьер. И все synchronized, lock и volatile в конечном счете создают именно такие "просто" барьеры.
Поэтому я могу абсолютно легально сказать, что запись в волатильную переменную A happens-before чтения волатильной переменной B — вещи, происходящие на низком уровне (флаши и инвалидации кэшей, барьеры) абсолютно идентичны.
Проблема в другом — пытаясь сделать это на разных волатильных переменных мы никак не можем узнать делаем ли мы чтение из волатильной переменной B до или после записи в волатильную A. То есть барьеры, флаши, happens-before — все это происходит. Но не происходит передача состояния. А с одной переменной у нас есть и happens-before, и передача состояния, по которому мы потом можем сделать if.
Re[5]: Вопрос на засыпку: синхронизация на разных volatile
Здравствуйте, seno, Вы писали:
S>Здравствуйте, maxkar, Вы писали:
S>В том то и дело, что создают! Вспомните, как релизованы мембары на уровни ОС? Разве там есть, условно, membar(variable)? Нет, там есть просто membar(). Он не ассоциирован ни с какой переменной или объектом. Это просто барьер. И все synchronized, lock и volatile в конечном счете создают именно такие "просто" барьеры.
Спецификация volatile диктует минимальные требования. Реализация на барьерах только потому, что другого нет. К тому же барьер барьеру рознь, и что он значит сильно зависит от архитектуры. Потом в реализации volatile это не только ограничения на уровне CPU, это еще и ограничения на уровне компилятора, которые к барьерам отношения не имеют.
S>Поэтому я могу абсолютно легально сказать, что запись в волатильную переменную A happens-before чтения волатильной переменной B — вещи, происходящие на низком уровне (флаши и инвалидации кэшей, барьеры) абсолютно идентичны.
В конетной реализации и архтектуре такое сказать можно, но не более. К тому же чисто теоретически, утрируя, может быть такая ситуация, каждая операция в потоке выполняется на разном процессоре. В этом случае барьер может сработать совсем не там...
S>Проблема в другом — пытаясь сделать это на разных волатильных переменных мы никак не можем узнать делаем ли мы чтение из волатильной переменной B до или после записи в волатильную A. То есть барьеры, флаши, happens-before — все это происходит. Но не происходит передача состояния. А с одной переменной у нас есть и happens-before, и передача состояния, по которому мы потом можем сделать if.
Какое это имеет отношение к volatile? В вашем примере "передачи состояния" просто не может быть.
Re: Вопрос на засыпку: синхронизация на разных volatile
Здравствуйте, seno, Вы писали:
S>Допустим, у нас есть следующий код: S>
S>int a = 0;
S>volatile boolean flag = false;
S>
S>И у нас есть два треда, которые выполняют следующие действия: S>Thread 1 S>
S>a = 1;
S>flag = true;
S>
S>Thread 2 S>
S>if (flag)
S> System.out.println(a);
S>
S>Фишка в том, что благодаря volatile переменной второй либо ничего не отпечатает, либо гарантированно отпечатает 1. Это аксиома, именно для этого семантика volatile правилась в обновленной JMM (http://www.ibm.com/developerworks/library/j-jtp02244/index.html).
Что помешает ему напечатать ноль? Может быть изменена последовательность выполнения инструкций:
flag = true;
a = 1;
И вторая нить выведет "0".
Re[2]: Вопрос на засыпку: синхронизация на разных volatile
Здравствуйте, techgl, Вы писали:
S>>Допустим, у нас есть следующий код: S>>
S>>int a = 0;
S>>volatile boolean flag = false;
S>>
S>>И у нас есть два треда, которые выполняют следующие действия: S>>Thread 1 S>>
S>>a = 1;
S>>flag = true;
S>>
S>>Thread 2 S>>
S>>if (flag)
S>> System.out.println(a);
S>>
S>>Фишка в том, что благодаря volatile переменной второй либо ничего не отпечатает, либо гарантированно отпечатает 1. Это аксиома, именно для этого семантика volatile правилась в обновленной JMM (http://www.ibm.com/developerworks/library/j-jtp02244/index.html).
T>Что помешает ему напечатать ноль? Может быть изменена последовательность выполнения инструкций: T>
T>flag = true;
T>a = 1;
T>
T>И вторая нить выведет "0".
Помешает пункт 17.5.4 JLS:
A write to a volatile field (§8.3.1.4) happens-before every subsequent read of that field.
А вообще, такое было возможно до java1.5
Re: Вопрос на засыпку: синхронизация на разных volatile
От:
Аноним
Дата:
20.07.13 11:16
Оценка:
Здравствуйте, seno, Вы писали:
S>Вопрос: каковы возможные результаты работы Thread 2 (можно выбрать несколько)? S>а) Ничего не отпечатает; S>б) Отпечатает 0; S>в) Отпечатает 1.
Может быть любой вариант, поскольку по happens-before синхронизированы только запись и чтение из ОДНОЙ и той же volatile переменной. То есть если мы прочитали volatile a, и узнали, что в ней появилось новое значение, то это означает, что наш поток будет видеть все изменения в памяти, произошедшие до этого присваивания. А если переменная другая — то вообще никакой связи нет. Реордеринг в терминах JMM вообще отсутствует как формальное понятие, его просто используют для абстрактного объяснения "вот мол процессор может выполнить инструкции вот так". На самом деле никто не знает, как процессор выполняет инструкции. Для нас имеет значение только видимость изменений в памяти, сделанных другим потоком.
Re[3]: Вопрос на засыпку: синхронизация на разных volatile