Метод Synchronized вызывает себя рекурсивно. Это сломано?

смысл этого вопроса заключается в том, чтобы проиллюстрировать, что 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. Очевидно, synchronized не блокирует доступ к тому же потоку.

это ошибка?

3 ответов


в Java синхронизированные блокировки являются reentrant.

Напомним, что поток не может получить блокировку принадлежит другому потоку. но поток может получить блокировку, которой он уже владеет. Разрешение потоку получать одну и ту же блокировку более одного раза обеспечивает повторную синхронизацию. Это описывает ситуацию, когда синхронизированный код, прямо или косвенно, вызывает метод, который также содержит синхронизированный код, и оба набора кода используют один и тот же замок. Без повторной синхронизации синхронизированному коду пришлось бы принять множество дополнительных мер предосторожности, чтобы избежать блокировки потока.

источник: см. Нижнюю часть на этой странице


синхронизированный метод должен иметь возможность получить блокировку объекта монитора. Объектом monitor является экземпляр (или класс для статического метода). Поток, который уже имеет блокировку, не должен получить ее снова. Так что да, это может вызвать stackoverflow (harhar).


с учебные пособия java:

когда один поток выполняет синхронизированный метод для объекта, все остальные потоки, которые вызывают синхронизированные методы для того же блока объекта (приостановить выполнение), пока первый поток не будет выполнен с объектом.

поэтому я думаю, что синхронизированное ключевое слово работало так, как ожидалось, и синхронизированный рекурсивный вызов совершенно законен (и работает) в java.