В сумме получается, что в C1 (client) happens-before не работает для volatile. Соответственно не работает код, который использует piggybacking. Например, ConcurrentHashmap#containsValue() или ConcurrentHashmap#containsKey().
Воркэруйнд — использовать C2 (server).
Теперь возникает вопрос — а нет ли там еще чего-нибудь похожего Казалось бы тривиальный кейс, странно, что не был протестивован.
Что-то я может быть тупляю, но в спецификации языка описанного поведения HB volatile не нашел, есть похожее, но там оба поля volatile. Упоминание нашел лишь в JSR 133 FAQ, хотя в самом JSR 133 тоже нет. Вот бы они там указали, где это описано. В статье есть ссылки на JSR и JLS, но как я сказал что-то пусто. Может быть это неявное поведение.
"Не волнуйся, голова! Теперь будет думать компьютер"
Гомер Джей Симпсон
Здравствуйте, UDI, Вы писали:
UDI>Здравствуйте, StanislavK, Вы писали:
UDI>Что-то я может быть тупляю, но в спецификации языка описанного поведения HB volatile не нашел, есть похожее, но там оба поля volatile. Упоминание нашел лишь в JSR 133 FAQ, хотя в самом JSR 133 тоже нет. Вот бы они там указали, где это описано. В статье есть ссылки на JSR и JLS, но как я сказал что-то пусто. Может быть это неявное поведение.
If x and y are actions of the same thread and x comes before y in program order, then hb(x, y).
A write to a volatile field (§8.3.1.4) happens-before every subsequent read of that field.
Используя два раза первое правило и один раз второе получаем требуемое.
Здравствуйте, RomikT, Вы писали:
RT>If x and y are actions of the same thread and x comes before y in program order, then hb(x, y).
Ну, дык, в одном же потоке действия, про разные никто ничего не говорит.
RT>A write to a volatile field (§8.3.1.4) happens-before every subsequent read of that field.
Только для этого волатильного поля (независимо от потока) порядок определен, но на порядок чтения-записи простых полей это никак не влияет. Также порядок сохраняется для n волатильных полей. 8.3.1.4 volatile Fields.
Так что вопрос, откуда это они взяли, еще открыт.
"Не волнуйся, голова! Теперь будет думать компьютер"
Гомер Джей Симпсон
Здравствуйте, UDI, Вы писали:
UDI>Здравствуйте, RomikT, Вы писали:
RT>>If x and y are actions of the same thread and x comes before y in program order, then hb(x, y).
UDI>Ну, дык, в одном же потоке действия, про разные никто ничего не говорит.
RT>>A write to a volatile field (§8.3.1.4) happens-before every subsequent read of that field.
UDI>Только для этого волатильного поля (независимо от потока) порядок определен, но на порядок чтения-записи простых полей это никак не влияет. Также порядок сохраняется для n волатильных полей. 8.3.1.4 volatile Fields.
UDI>Так что вопрос, откуда это они взяли, еще открыт.
Ещё раз:
int a;
volatile int b;
void inThreadA() {
a = 1; // action1 (write to a)
b = 1; // action2 (write to b)
}
void inThreadB() {
if (b == 1) { // action3 (read from b)
assert a == 1; // action 4 (read from a)
}
}
Внутри каждого потока есть program order, по первому правилу имеем hb(action1, action2) и hb(action3, action4). Так как b volatile, то по второму правилу имеем hb(action2, action3). А из транзитивности отношения hb получаем что hb(action1, action4) несмотря на то, что a не volatile.
По приведённой же вами ссылке обе переменные должны быть volatile поскольку они читаются и пишутся в одинаковом порядке.
RT>int a;
RT>volatile int b;
RT>void inThreadA() {
RT> a = 1; // action1 (write to a)
RT> b = 1; // action2 (write to b)
RT>}
RT>void inThreadB() {
RT> if (b == 1) { // action3 (read from b)
RT> assert a == 1; // action 4 (read from a)
RT> }
RT>}
RT>
RT>Внутри каждого потока есть program order, по первому правилу имеем hb(action1, action2) и hb(action3, action4). Так как b volatile, то по второму правилу имеем hb(action2, action3). А из транзитивности отношения hb получаем что hb(action1, action4) несмотря на то, что a не volatile.
У вас action2 и action3 это не одно и то же действие, и уж тем более не в одном потоке. Т.е.
If hb(x, y) and hb(y, z), then hb(x, z)
не выполняется.
RT>По приведённой же вами ссылке обе переменные должны быть volatile поскольку они читаются и пишутся в одинаковом порядке.
В примере, приведенном в спеке по моей ссылке, в ином случае (без 2-x volatile) просто никто никому ничего не гарантирует. Вопрос, почему же все-таки сообщество посчитало, что приведенное поведение гарантировано. Хотя может быть я чего-то не замечаю и вы правы. К сожалению, сейчас не могу продолжить дискуссию — очень поздно.
"Не волнуйся, голова! Теперь будет думать компьютер"
Гомер Джей Симпсон
Здравствуйте, UDI, Вы писали:
UDI>Здравствуйте, RomikT, Вы писали: RT>>Внутри каждого потока есть program order, по первому правилу имеем hb(action1, action2) и hb(action3, action4). Так как b volatile, то по второму правилу имеем hb(action2, action3). А из транзитивности отношения hb получаем что hb(action1, action4) несмотря на то, что a не volatile.
UDI>У вас action2 и action3 это не одно и то же действие, и уж тем более не в одном потоке. Т.е. UDI>
UDI>If hb(x, y) and hb(y, z), then hb(x, z)
UDI>не выполняется.
Ну ёлки-палки, используйте транзитивность два раза
hb(action1, action2) and hb(action2, action3) and hb(action3, action4) => hb(action1, action4)
UDI>В примере, приведенном в спеке по моей ссылке, в ином случае (без 2-x volatile) просто никто никому ничего не гарантирует. Вопрос, почему же все-таки сообщество посчитало, что приведенное поведение гарантировано. Хотя может быть я чего-то не замечаю и вы правы. К сожалению, сейчас не могу продолжить дискуссию — очень поздно.
почему, все же четко: главный поток (main) выставляет обычную переменную "b" и волатильную "a", второй же поток (T2) ждет выставления "a", сразу после читает ее, и потом обращается к "b" — по спеке (новой 133) T2 должен увидеть все обновленные переменные до "write to a" в том числе и неволатильные.
UDI>У вас action2 и action3 это не одно и то же действие, и уж тем более не в одном потоке. Т.е.
На самом деле правильно замечено насчет другого потока. Но там вот какое дело:
A write to a volatile variable v (§8.3.1.4) synchronizes-with all subsequent reads of v by any thread (where "subsequent" is defined according to the synchronization order).
плюс вот это:
If an action x synchronizes-with a following action y, then we also have hb(x, y).
Дают в сумме свзязь потоков и этот вывод:
A write to a volatile field (§8.3.1.4) happens-before every subsequent read of that field.