Question

I'm new to Java and OOP. I'm reading concurrency in java and in chapter 2, it talks about re-entrancy. I don't quite understand how a deadlock situation would occur. Can some one break this situation down for me to understand the details line by line?

Thank you in advance.

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.

public class Widget {
    public synchronized void doSomething() {
      ...
    }
}
public class LoggingWidget extends Widget {
   public synchronized void doSomething() {
      System.out.println(toString() + ": calling doSomething");
      super.doSomething();
   }
}

and the thread would permanently stall waiting for a lock it can never acquire.

How, why, which thread?

Was it helpful?

Solution

How, why, which thread?

The thread that deadlocks is the thread that attempts to acquire the lock; i.e. this one.


How:

  1. Obtain reference to a LoggingWidget instance

  2. Call doSomething() on the instance

  3. The call to LoggingWidget.doSomething() acquires the lock on the instance, since the method is synchronized.

  4. The called method then calls super.doSomething().

  5. The call to Widget.doSomething() tries to acquire the lock on the instance (again!), since the supertype method is also synchronized.

At step #5. the current thread attempts to acquire a primitive lock on an instance that it has already locked. If primitive locks were not reentrant, then that would deadlock ...

  • "some thread" already holds the lock on the object, so we have to wait for that thread to release the lock,

  • that "some thread" is the current thread ... and we are not going to release the lock until we return from the LoggingWidget.doSomething()

  • but that can't happen until after we've completed the call to Widget.doSomething()

  • and that can't happen until after we've acquired the lock

  • ..... DEADLOCK!

But fortunately, there is no deadlock in reality. The fact that primitive locks are reentrant means that step #5 doesn't need to acquire the lock (it already has it), and the whole waiting-for-myself-to-do-something-that-cannot-happen scenario simply does not arise.


Why: By the inescapable power of Murphy's Law. "If anything can go wrong, it will." :-)

Which thread: According to Finagle's Law, it will be the one that causes the most damage. :-)

These so-called "laws" have not been demonstrated scientifically. But that doesn't mean you should ignore what they are intended to teach.

OTHER TIPS

I think you got confused by the author's use of the word re-entrancy. By "locks are re-entrant" he meant that if your thread holds a lock already, Java allows entering a synchronized method from inside another synchronized method, or re-entering the same synchronized method recursively.

This is different from a more common meaning of re-entrancy as the ability of a piece of code to be re-entered, concurrently or on the same thread when the status is preserved.

Without re-entrancy in the author's meaning the synchronized of the super.doSomething() would block on trying to acquire the lock held by this.doSomething(), because they block on the same object (namely, on this).

You have two methods that lock on the same object - an instance of LoggingWidget - that would be run by the same thread.

  1. An instance of LoggingWidget is created
  2. The doSomething method defined in the LoggingWidget class is called against that instance
  3. A lock on the instance of LoggingWidget is obtained
  4. The println statement is executed
  5. The doSomething method of the LoggingWidget class calls the doSomething method defined in the Widget class
  6. A lock on the same instance of LoggingWidget is needed to proceed, but it's already held by the thread executing the first method

If locks were not re-entrant, a deadlock would occur at step 6 since the lock on the instance is already held. But since built-in Object locks in Java are re-entrant, step 6 continues as normal since the lock that is needed is already held by the executing thread.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top