可重入锁 - 实践中的Java并发

时间:2015-01-12 10:53:25

标签: java concurrency locking reentrancy

以下是一些示例代码,用于在实践中对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中非常自然的代码,in   哪个子类重写同步方法然后调用   超类方法,会死锁。因为doSomething方法在   Widget和LoggingWidget都是同步的,每个都尝试获取   在继续之前锁定Widget。但如果内在锁定   不可重入,对super.doSomething的调用永远不会   获得锁定,因为它将被视为已经持有,并且   线程将永久停止等待锁定它永远不会   获得。在这样的情况下,重入可以使我们免于死锁。

2 个答案:

答案 0 :(得分:7)

我需要看一段摘录给你一个具体的答案。您可以以不同的方式实例化这些类。锁定在对象上,因此引用无关紧要。为了说明......

这个类结构与你的模仿结构非常相似。

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上。

[编辑]

根据您的具体情况编辑我的答案。

答案 1 :(得分:4)

是的,作者可以互换地使用LoggingWidget / Widget,因为根据OOP继承原则,LoggingWidget对象也是一个Widget类对象。在该示例中,将仅创建一个对象实例,并将其用作同步监视器以重新输入。