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?
To support the rest of the answers here with some actual proof I ran your code in a loop and it deadlocked 82 times out of 100 tries, so your code most definitely still deadlocks.
You are mixing up things here.
The fact that a piece of code can lead into a deadlock situation doesn't necessarily mean that you receive a deadlock each and any time that code runs.
That is one of the aspects that makes multi-threading such a hard topic: if you run your code once, or 10 times, or a 100 times, and everything "works"; it is still possible that it will fail the next time.
In other words: try putting that code in a loop on the outermost level, and sooner or later (probably sooner; if you don't do much "sleeping") you should hit the deadlock!
If things were that easy and deadlocks could be detected that easily, we wouldn't need all those books and libraries and ideas how to deal with multi-threading ...
The deadlock is not dependent on the println function at all. It's caused by the two threads trying to access each other, and locking on each other.
The change from format to println will have introduced sufficient latency in the program to allow the threads to lock each other without colliding, i.e., deadlocking. So you haven't really fixed it; you've just added some latency that means the threads don't deadlock.
There is no difference in whether you use
System.out.print
orSystem.out.format
: they're basically doing the same thing.The deadlock occurs here if execution of
Gaston.bow(Alphonse)
is started between the start ofAlphonse.bow(Gaston)
andbower.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
andbower.backBack(Alphonse)
complete beforeGaston.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:
(*) 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.It's an application of the Birthday paradox: if you've got just two monitors, the chance of collision is something like 10^-18; but if you've got >77k monitors, a collision is more likely than not.