可重入锁 - Java并发实践

5

这是《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();
}
}

该书解释道,在上述代码中,“由于Widget和LoggingWidget的doSomething方法都是同步的,每个方法在继续之前都会尝试获取Widget上的锁。”
我运行了上述代码以观察内部锁。上述引用似乎暗示线程在Widget对象上获取内部锁,但是我观察到的是线程在LoggingWidget上获取锁。我不确定如何验证获取次数,因此无法观察到。
该书是否在交替使用LoggingWidget/Widget名称,还是我应该特别观察Widget对象上的锁?
编辑:完整摘录
“可重入性有助于封装锁定行为,从而简化面向对象并发代码的开发。如果没有可重入锁,则列表2.7中非常自然的代码将导致死锁,其中子类覆盖同步方法,然后调用超类方法。由于Widget和LoggingWidget中的doSomething方法都是同步的,每个方法在继续之前都会尝试获取Widget上的锁。但是,如果内部锁不可重入,则对super.doSomething的调用永远无法获取锁,因为它被认为已经持有,并且线程将永久停滞等待永远无法获取的锁。可重入性在这种情况下避免了死锁。”

一个线程死锁 - 对我来说看起来很奇怪 - gstackoverflow
2个回答

9

我需要查看摘要以便给您一个具体的答案。您可以以不同的方式实例化这些类。锁定是在对象上持有的,因此引用的内容并不重要。为了说明这一点...

这个类结构与您的类结构非常相似。

public class GenericTest {
    public static void main(String... args) {
        Sub sub = new Sub();
        sub.go();
    }

    public synchronized void go() {
        System.out.println("Parent");
    }
}

class Sub extends GenericTest {
    @Override
    public synchronized void go() {
        System.out.println("Child");
        super.go();
    }
}

运行此程序并在使用您喜欢的方法(例如System.in.read())获取锁之后停止执行其他行。找到Java程序的pid并在Jconsole中打开它。转到threads部分,并在每次获取锁时将其突出显示。您将看到以下跟踪信息。
my.package.common.GenericTest.go(GenericTest.java:30)
   - locked my.package.common.Sub@4c767286
my.package.common.Sub.go(GenericTest.java:42)
   - locked my.package.common.Sub@4c767286

由于这种方法是成员变量,所以锁定的是执行该方法的当前对象(this)。请注意,两个锁都在Sub@4c767286上。
[编辑]
编辑我的答案以适应您的特定情况。

为了清晰起见,以下是完整摘录。但你和@Semyon的回答已经让问题变得明确了。不幸的是,只能选择一个回答作为答案。 - sotn

5

是的,作者使用LoggingWidget/Widget交替使用,因为根据OOP继承原则,LoggingWidget对象也是Widget类对象。在示例中,只会创建一个对象实例,并将其用作同步监视器以重新进入。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接