Вот пример кода для реентерабельной блокировки из статьи «Параллелизм Java на практике»:
class Widget {
public synchronized void doSomething() {
System.out.println(toString() + ": calling superclass doSomething");
}
}
class LoggingWidget extends Widget {
public synchronized void doSomething() {
System.out.println(toString() + ": calling subclass doSomething");
super.doSomething();
}
}
В книге объясняется, что в приведенном выше коде ... «Поскольку методы doSomething в Widget и LoggingWidget оба синхронизированы, каждый из них пытается получить блокировку виджета, прежде чем продолжить».
Я запустил приведенный выше код, чтобы увидеть внутреннюю блокировку. Вышеприведенная цитата, по-видимому, подразумевает, что поток получает внутреннюю блокировку объекта Widget, но я заметил, что поток получает блокировку LoggingWidget. Я не знаю, как проверить количество приобретений, поэтому не мог этого наблюдать.
Использует ли книга имена LoggingWidget / Widget как взаимозаменяемые, или я должен специально наблюдать блокировку объекта Widget?
Изменить: полный отрывок
Повторная входимость облегчает инкапсуляцию поведения блокировки и, таким образом, упрощает разработку объектно-ориентированного параллельного кода. Без повторных блокировок очень естественно выглядящий код из Листинга 2.7, в котором подкласс переопределяет синхронизированный метод, а затем вызывает метод суперкласса, окажется в тупике. Поскольку методы doSomething в Widget и LoggingWidget оба синхронизированы, каждый из них пытается получить блокировку виджета перед продолжением. Но если внутренние блокировки не были реентерабельными, вызов super.doSomething никогда не смог бы получить блокировку, потому что он считался бы уже удерживаемым, и поток постоянно зависал бы в ожидании блокировки, которую он никогда не сможет получить. Повторная входимость спасает нас от тупика в подобных ситуациях.