简体   繁体   中英

Reentrant lock - Java concurrency in practice

Here is some sample code for reentrant locking from 'Java concurrency in practice':

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

The book explains that in the above code... "Because the doSomething methods in Widget and LoggingWidget are both synchronized, each tries to acquire the lock on the Widget before proceeding."

I ran the above code to observe the intrinsic lock. The above quote seems to imply that a thread acquires an intrinsic lock on the Widget object, but what I observed was that the thread acquires a lock on LoggingWidget. I am not sure how to verify the acquisition count, so wasn't able to observe that.

Is the book using the names LoggingWidget/Widget interchangeably or should I be observing a lock on the Widget object specifically?

Edit: Full Excerpt

Reentrancy facilitates encapsulation of locking behavior, and thus simplifies the development of object-oriented concurrent code. Without reentrant locks, the very natural-looking code in Listing 2.7 , in which a subclass overrides a synchronized method and then calls the superclass method, would deadlock. Because the doSomething methods in Widget and LoggingWidget are both synchronized, each tries to acquire the lock on the Widget before proceeding. But if intrinsic locks were not reentrant, the call to super.doSomething would never be able to acquire the lock because it would be considered already held, and the thread would permanently stall waiting for a lock it can never acquire. Reentrancy saves us from deadlock in situations like this.

I'd need to see the excerpt to give you a specific answer. You can instantiate those classes in different ways. The lock is held on the object, so what the reference is does not matter. To illustrate that...

This class structure closely mimics yours.

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

Run this program and stop executing further lines after acquiring a lock with a method of your pleasing (say System.in.read()). Find the pid of the java program and open it in Jconsole. Move to the threads section and highlight it every time the lock was acquired. You'll see the following traces.

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

Since this method is a member variable, the lock is on the current object ( this ) that executed the method in question. Notice how both locks are on Sub@4c767286 .

[EDIT]

Edited my answer to suit your specific case.

Yes the author uses LoggingWidget/Widget interchangeably because according to the OOP inheritance principle LoggingWidget object is a Widget class object too. In the example only one object instance will be created and it will be used as a synchronization monitor to re-enter.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM