Блокировка повторного входа - PullRequest
11 голосов
/ 09 июля 2009

Немного помощи, пожалуйста, рассмотрите фрагмент кода ниже.

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 ]

9 голосов
/ 09 июля 2009

Вы ошибаетесь - блокировки получены на уровне экземпляр . В вашем примере только одна блокировка, потому что создается только один экземпляр, когда вы говорите:

Widget w = new LoggingWidget

Вы можете просматривать блокировки (также известные как мониторы , мьютексы или семафоры ) как индивидуально "прикрепленные" к каждому экземпляру объекта в JVM .

Если бы у вас был другой метод synchronized в подклассе LoggingWidget, вы бы увидели, что это правда. Невозможно вызвать этот (новый) метод и метод doSomething одновременно [с разными потоками на одном объекте].

Это также относится и к другому методу synchronized в суперклассе (т.е. на него не влияют никакие переопределенные методы).

5 голосов
/ 09 июля 2009
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 (), этот экземпляр уже заблокирован.

1 голос
/ 10 июля 2009

Reentrancy работает, сначала получив замок. Когда один поток получает блокировку, он известен в jvm. При вводе блока кода, который синхронизирован с потоком, который в данный момент удерживает блокировку, им разрешается продолжать работу без повторного получения блокировки. Затем jvm увеличивает счетчик при каждом повторном входящем действии, еще больше уменьшаясь при выходе из этого блока, пока счетчик не станет равным нулю. Когда счетчик равен нулю, блокировка снимается.

1 голос
/ 09 июля 2009

Существует только один экземпляр для блокировки, экземпляр LoggingWidget, фактический экземпляр Widget.

никогда не существует.

Блокировка получается, когда вы вызываете LoggingWidget.doSomething(), и поскольку у вас уже есть блокировка при вызове super.doSomething(), этот метод выполняется как обычно.

0 голосов
/ 10 июля 2009

B.Goetz - «Параллельность JJava на практике», если внутренние блокировки не были повторно введены, вызов super.doSomething никогда не сможет получить блокировку, потому что она будет считаться уже удержанной, и поток будет постоянно останавливаться, ожидая замок, который он никогда не сможет приобрести. В таких ситуациях спасение спасает нас от тупика.

...