Почему `synchronized (new Object ()) {}` не работает?
В следующем коде:
class A {
private int number;
public void a() {
number = 5;
}
public void b() {
while(number == 0) {
// ...
}
}
}
Если вызывается метод b, а затем запускается новый поток, который запускает метод a, то гарантируется, что метод b никогда не увидит измененияnumber
и поэтомуb
никогда не может прекратить.
Конечно, мы могли бы сделатьnumber
volatile
чтобы решить это. Однако по академическим причинам давайте предположим, чтоvolatile
не вариант:
JSR-133 часто задаваемые вопросы говорит нам:
После выхода из синхронизированного блока мы отпускаем монитор, которыйимеет эффект сброса кеша в основную память, так что записи, сделанные этим потоком, могут быть видны другим потокам. Прежде чем мы сможем войти в синхронизированный блок, мы приобретаем монитор, которыйимеет эффект аннулирования кэша локального процессора так что переменные будут перезагружены из основной памяти.
Звучит так, будто мне нужны обаa
а такжеb
входить и выходить из любогоsynchronized
-Блок вообще, независимо от того, какой монитор они используют. Точнее это звучит так ...:
class A {
private int number;
public void a() {
number = 5;
synchronized(new Object()) {}
}
public void b() {
while(number == 0) {
// ...
synchronized(new Object()) {}
}
}
}
... устранит проблему и гарантирует, чтоb
увидим изменениеa
и, таким образом, также в конечном итоге прекратится.
Однако часто задаваемые вопросы также ясно заявляют:
Другим следствием является то, что следующий шаблон, который некоторые люди используют для создания барьера памяти, не работает:
synchronized (new Object()) {}
На самом деле это не работает, и ваш компилятор может полностью удалить его, потому что компилятор знает, что никакой другой поток не будет синхронизироваться на том же мониторе. Вы должны установить отношение «до и после» для одного потока, чтобы увидеть результаты другого.
Теперь это сбивает с толку. Я думал, что синхронизированный оператор вызовет сброс кешей. Конечно, он не может сбросить кэш в основную память таким образом, чтобы изменения в основной памяти могли видеть только потоки, которые синхронизируются на одном мониторе, тем более что для volatile, который в основном делает то же самое, нам даже не нужен монитор, или я там ошибаюсь? Так почему же это неоперация и не вызываетb
расторгнуть по гарантии?