Здравствуйте, denis.zhdanov, Вы писали:
DZ>Здравствуйте, Golovach Ivan, Вы писали:
GI>>МММ ... Я еще раз пересмотрел 17-ю главу jls.third_edition [http://java.sun.com/docs/books/jls/third_edition/html/memory.html] и не обнаружил правила запрещающего следующее:
GI>>Пусть один поток вызывает метод doIt() у общего объекта
GI>>GI>>class Shared {
GI>> public volatile int vol = 0;
GI>> public int nonvol = 0;
GI>> public void doIt() {
GI>> vol = 1;
GI>> nonvol = 1;
GI>> }
GI>>}
GI>>
GI>>а второй поток проверяет условие у этого же общего объекта
GI>>GI>>Shared shared = ...
GI>>if (shared.vol == 0 && shared.nonvol == 1) {...}
GI>>
GI>>и, как я понимаю, это условие МОЖЕТ ВЫПОЛНИТСЯ. Т.е. с точки зрения второго потока будет виден реордеринг записи vol и записи nonvol.
GI>>Для себя я понял JMM так, что нельзя нарушать happend-before и casuality. Оно и не нарушается в данном примере.
GI>>Вполне допускаю, что я не прав. Но тогда я заблуждаюсь искренне
.
GI>>Вы могли бы обосновать свое мнения на моем примере основываясь на спецификации языка?
DZ>В приведенном примере указанное условие может выполниться не из-за reordering, а из-за обыкновенного race condition. Т.е. последовательность событий может быть такая:
DZ>
DZ>Thread t1 calls doIt();
DZ>Thread t2 checks 'shared.vol == 0 (true)';
DZ>t1 executes 'vol = 1; nonvol = 1';
DZ>t2 checks 'shared.nonvol ==1 (true)';
DZ>
Согласен. Предлагаю переформулировку. Мне кажется, что переписанное условие
if (shared.nonvol == 1 && shared.vol == 0) {...}
может выполнится. По крайней мере, я не нашел запрета на это, но нашел подтверждение в виде Roach Motel Principle.
И если это так, что с точки зрения второго потока он будет наблюдать явление, трактуемое как реордеринг записи в vol и nonvol. Т.е. то, что категорически отвергал remark.
DZ>В указанном примере happen-before вообще не работает, потому что 'nonvol = 1' выполняется после записи в volatile-переменную.
Ну да, хаппенд-бефора тут нет. Вопрос же был — возможен ли хоть какой-то реордеринг волатайл и не-волатайл операций.
Собственно, я опираюсь на Roach Motel Principle (тут, например —
http://osdir.com/ml/java.jsr.166-concurrency/2008-04/msg00018.html — Автор Джереми Мэйсон):
""Roach Motel" applies to synchronized blocks -- instructions can move
into them, but not out. So, normal accesses can be moved from after a
monitorexit to before it, and from before a monitorenter to after it.
That's the roach motel principle.
The same reasoning applies to volatiles under the assumption that a
volatile write is "the same" as a monitorexit and that a volatile read
is "the same" as a monitorenter.
So, your answer is that you *can* move normal accesses that occur after
a volatile write to before it."
З.Ы. по Roach Motel Principle есть несколько постов на concurrency-interest (надо искать по всему архиву, но вот навскидку —
http://www.cs.umd.edu/~pugh/java/memoryModel/archive/0773.html).