У меня есть вопрос относительно модели памяти Java. В следующем примере:
action 1
action 2
synchronized(monitorObject) { //acquire
action 3
} //release
action 4
acquire
и release
могут быть любыми синхронизациями - с реброми (блокировка, разблокировка, запуск потока, объединение потока, обнаружение прерывания потока, volatile-write, volatile-read и т.д.)
Гарантировано ли, что action 3
невозможно перенести до приобретения и не может быть перемещено после выпуска?
И гарантируется ли, что action 2
не может быть перемещен после приобретения (ни до, ни после релиза), и что action 4
не может быть перемещен до релиза (до и после приобретения)?
Итак, синхронизируются ли с границами "двунаправленные барьеры" для действий по переупорядочению компилятора?
РЕДАКТИРОВАТЬ 1 Меня это беспокоит, потому что если синхронизация - с ребрами не были двунаправленными переупорядочивающими барьерами, то компилятор мог бы просто создать тупик путем перемещения блокировки в другие.
Или двунаправленные переупорядочивающие барьеры, которые даже не нужны для предотвращения этого, потому что блокировка не может быть переброшена в другие, потому что это изменит порядок синхронизации?
РЕДАКТИРОВАТЬ 2 Действия 1, 2, 3 и 4 являются "межпотоковыми действиями", как определено JMM.
РЕДАКТИРОВАТЬ 3 Вот пример, показывающий, как переупорядочение может вызвать тупик:
x и y - общие переменные, а syncA и syncB могут быть получены любым другим потоком. Но с помощью следующего кода невозможно установить тупик.
/* 1 */ synchronized(syncA) {
/* 2 */ x = 1;
/* 3 */ }
/* 4 */ y = 0;
/* 5 */ synchronized(syncB) {
/* 6 */ y = 1;
/* 7 */ }
Если, однако, приобретение syncA переупорядочивается в блок syncB, это может вызвать тупик:
y = 0;
synchronized(syncB) {
y = 1;
synchronized(syncA) {
x = 1;
}
}
Я думаю, что это не законное преобразование компилятора, потому что это изменит порядок синхронизации. Правильно ли я с этим предположением? Какая часть модели памяти Java (JMM) разрешает/запрещает это?