Синхронизированный метод вызывает себя рекурсивно. Это сломано?
Цель этого вопроса - проиллюстрировать, что Java работает не так, как я ожидал.
Как вы ожидаете, что следующий код будет вести себя?
public class SynchTester {
private static SynchTester synchTester;
public synchronized static SynchTester getSynchTester(){
if(synchTester==null){
synchTester = new SynchTester();
}
return synchTester;
}
private SynchTester() {
SynchTester myTester = getSynchTester();
}
public static void main(String[] args) {
SynchTester tester = SynchTester.getSynchTester();
}
}
Я ожидаю, что он будет висеть с тупиком, ожидающим завершения рекурсии, но вместо этого он бросает StackOverflow. Очевидно, синхронизация не блокирует доступ к тому же потоку.
Это ошибка?
Ответы
Ответ 1
В Java синхронизированные блокировки реентерабельны.
Вспомните, что нить не может получить блокировку, принадлежащую другому потоку. Но поток может получить блокировку, которой он уже владеет. Разрешить потоку получать один и тот же замок более одного раза, обеспечивает повторную синхронизацию. Это описывает ситуацию, когда синхронизированный код, прямо или косвенно, вызывает метод, который также содержит синхронизированный код, и оба набора кода используют одну и ту же блокировку. Без повторной синхронизации синхронизированный код должен принять много дополнительных мер предосторожности, чтобы избежать блокировки потока.
Источник: см. нижнюю часть этой страницы
Ответ 2
Синхронизированный метод должен иметь возможность блокировки объекта монитора. Объектом монитора является экземпляр (или класс для статического метода). Нить, которая уже имеет блокировку, не нуждается в ее повторении. Так что да, это может вызвать stackoverflow (harhar).
Ответ 3
из java tutorials:
Когда один поток выполняет синхронизированный метод для объекта, все другие потоки, которые вызывают синхронизированные методы для одного и того же объекта (приостановить выполнение) до тех пор, пока первый поток не будет выполнен с объектом.
Итак, я думаю, что ключевое слово syncronized работало так, как ожидалось, и синхронизированный рекурсивный вызов совершенно легален (и работает) в java.