Steve Steve - 1 month ago 20
Java Question

Mutithreading with System.out.format and System.out.println

I came across this example on Oracle's Java Tutorial describing Deadlock in multi threading scenarios.

So in this example I made following change at line 17 and line 18.

public class DeadLock {
static class Friend {
private final String name;

public Friend(String name) {
this.name = name;
}

public String getName() {
return this.name;
}

public synchronized void bow(Friend bower) {
//My Changes
//System.out.format("%s: %s" + " has bowed to me!%n", this.name, bower.getName()); //Line 17
System.out.println(this.name + ": " + bower.getName() + " has bowed to me!"); //Line 18
bower.bowBack(this);
}

public synchronized void bowBack(Friend bower) {
System.out.format("%s: %s" + " has bowed back to me!%n", this.name, bower.getName());
}
}

public static void main(String[] args) {
final Friend alphonse = new Friend("Alphonse");
final Friend gaston = new Friend("Gaston");
new Thread(new Runnable() {
@Override
public void run() {
alphonse.bow(gaston);
}
}).start();

new Thread(new Runnable() {
@Override
public void run() {
gaston.bow(alphonse);
}
}).start();
}
}


Upon doing these changes the program terminated successfully without causing deadlock and printed following output

Alphonse: Gaston has bowed to me!
Gaston: Alphonse has bowed back to me!
Gaston: Alphonse has bowed to me!
Alphonse: Gaston has bowed back to me!


So my question is- why did it behave like this? How did the println statement prevent the deadlock?

Answer

There is no difference in whether you use System.out.print or System.out.format: they're basically doing the same thing.

The deadlock occurs here if execution of Gaston.bow(Alphonse) is started between the start of Alphonse.bow(Gaston) and bower.bowBack(Alphonse) (or vice versa): the two threads are waiting for a monitor held by the other, and thus deadlock occurs.

This happens inconsistently, because it depends upon a subtle timing issue, depending upon how the threads are scheduled - it is possible that Alphonse.bow and bower.backBack(Alphonse) complete before Gaston.bow is executed, so it looks like there is no deadlock.

The classic way to fix this is to order the lock acquisition, so that the first the same lock is acquired first every time; this prevents the possibility of deadlock:

public void bow(Friend bower) {  // Method no longer synchronized.
  int firstHash = System.identityHashCode(this);
  int secondHash = System.identityHashCode(bower);

  Object firstMonitor = firstHash < secondHash ? this : bower;
  Object secondMonitor = firstHash < secondHash ? bower : this;
  synchronized (firstMonitor) {
    synchronized (secondMonitor) {
      // Code free (*) of deadlocks, with respect to this and bower at least.
    }
  }
}

(*) It's not quite guaranteed to be deadlock free, since System.identityHashCode can return the same value for distinct objects; but that's reasonably unlikely.