Will the following code execute successfully?

Viewed 126

In Brian Goetz's Java Concurrency in Practice, there is following example which explains the reentrancy of the locks in Java as:

public class Widget {
  public synchronized void doSomething() {
    ...
  }
}

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

It says that because of the reentrancy of locks in Java, the above code will not lead to a deadlock, since the locks are attained on per-thread basis rather than per-invocation basis.

However, if we twist the example a bit like:

public class XYZ {
  public synchronized void someFunction() {
    ...
  }
}

public class SomeClass {
  private XYZ xyz;
  ...
  public synchronized void functionCalled() {
    ...
    xyz.someFunction();
  }
}

We call the functionCalled() of SomeClass and the lock is obtained on the object of SomeClass. Now, will the someFunction() get called or in other terms will the thread enter the someFunction() of xyz class. Will the synchronized function of XYZ class ask for lock on the object of XYZ class? I am a bit confused. Please clarify.

2 Answers

Yes, the code above will ask for a lock on both SomeClass and the xyz object. This causes no problems however.

The xyz object is private, so there is no possibility that any thread will first lock xyz before invoking functionCalled(), thus there is no possibility of dead lock since the two locks here are always called in the order SomeClass -> xyz.

The easiest way to figure out of it succeeds or not is to simply run it. I rewrote you code your code to a working example and it does in fact print out "Hello" in both cases.

public class XYZ {

  public void someFunction() {
    synchronized (this) {
        System.out.println("Hello");
    }
  }
}

public class SomeClass {
  private XYZ xyz = new XYZ();

  public void functionCalled() {
    synchronized (this) {
      xyz.someFunction();
    }
  }

  public static void main(String[] args) {
    new SomeClass().functionCalled();
  }
}

I also rewrote your methods to use synchronise blocks instead. They way they work is that no threads can enter a block if another thread is inside it and the object (lock) is the same. Here you can see that both functions synchronise over this which refers to two different objects since the two methods belong to two different classes. There is no conflict here.

functionCalled is synchronised over an instance of SomeClass

someFunction is synchronised over an instance of XYZ

This is different from the example in your book though, what I think the point of the example is when the locks are in fact the same! This could in some environments cause a deadlock, but as stated not in Java. Take this example.

public class XYZ {

  public void someFunction() {
    synchronized (this) {
        System.out.println("Hello");
    }
  }
}

public class SomeClass {
  private XYZ xyz = new XYZ();

  public void functionCalled() {
    synchronized (xyz) {
      xyz.someFunction();
    }
  }

  public static void main(String[] args) {
    new SomeClass().functionCalled();
  }
}

It might look similar, but in this case the functionCalled synchronises over the same object, aka. they are using the same object as lock! The point here is that the current thread already has the lock on the object since locks are on a per-thread basis so it can safely enter the synchronised block and does not try to acquire the lock again. If it did get the lock on a per-invocation basis it would try to get the lock again and get stuck since the first invocation already owns the lock.

Related