Блокировка повторного входа
Немного помощи, пожалуйста, рассмотрите немного кода ниже.
public class Widget {
public synchronized void doSomething() {
...
}
}
public class LoggingWidget extends Widget {
public synchronized void doSomething() {
System.out.println(toString() + ": calling doSomething");
super.doSomething();
}
}
Я читал, что при вызове doSomething() в LoggingWidget JVM будет пытаться сначала получить блокировку для LoggingWidget, а затем для Widget.
Мне любопытно узнать причину. Это потому, что JVM знает, что doSomething() имеет вызов super.doSomething(), или потому что вызов метода подкласса также всегда будет блокировать суперкласс.
ура
5 ответов
Вы ошибаетесь - блокировки получаются на уровне экземпляра. В вашем примере есть только одна блокировка, потому что создается только один экземпляр, когда вы говорите:
Widget w = new LoggingWidget
Вы можете просмотреть блокировки (также известные как мониторы, мьютексы или семафоры) как индивидуально "прикрепленные" к каждому экземпляру объекта в JVM
,
Если бы у вас был другой synchronized
метод на LoggingWidget
подкласс, вы бы увидели, что это правда. Было бы невозможно вызвать этот (новый) метод и doSomething
метод одновременно [с разными потоками на одном объекте].
Это также верно для другого synchronized
метод в суперклассе (то есть на него не влияют никакие переопределенные методы).
public synchronized void doSomething() {
System.out.println(toString() + ": calling doSomething");
super.doSomething();
}
такой же как:
public void doSomething() {
synchronized (this) {
System.out.println(toString() + ": calling doSomething");
super.doSomething();
}
}
Вы блокируете экземпляр, а не класс. Поэтому, когда вызывается super.doSomething(), этот экземпляр уже заблокирован.
Существует только один экземпляр для блокировки, экземпляр LoggingWidget
нет фактического экземпляра Widget
,
Блокировка получается при звонке LoggingWidget.doSomething()
и как у вас уже есть замок при звонке super.doSomething()
этот метод выполняется как обычно.
Reentrancy работает, сначала получив замок. Когда один поток получает блокировку, он известен в jvm. При вводе блока кода, который синхронизирован с потоком, который в данный момент удерживает блокировку, им разрешается продолжать без повторного получения блокировки. Затем jvm увеличивает счетчик при каждом поступающем действии, еще больше уменьшаясь при выходе из этого блока до тех пор, пока счетчик не станет равным нулю. Когда счетчик равен нулю, блокировка снимается.
Б.Гетц - "Параллелизм JJava на практике", если внутренние блокировки не были повторно введены, вызов super.doSomething никогда не сможет получить блокировку, потому что она будет считаться уже удержанной, и поток будет постоянно останавливаться в ожидании блокировки. никогда не сможет приобрести. Reentrancy спасает нас от тупика в таких ситуациях.