I’ve found that including a call to System.out.format in the classic Java Deadlock Tutorial will prevent deadlock from occurring, and I can’t figure out why.
The code below is the same as that of the tutorial, with the addition to main of System.out.format("Hi, I'm %s...no deadlock for you!\n\n", alphonse.getName());
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) {
System.out.format("%s: %s has bowed to me!\n",
this.name, bower.getName());
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) throws InterruptedException {
final Friend alphonse = new Friend("Alphonse");
final Friend gaston = new Friend("Gaston");
System.out.format("Hi, I'm %s...no deadlock for you!\n\n", alphonse.getName());
new Thread(new Runnable() {
public void run() { alphonse.bow(gaston); }
}).start();
new Thread(new Runnable() {
public void run() { gaston.bow(alphonse); }
}).start();
}
}
Here’s the output:
Hi, I'm Alphonse...no deadlock for you!
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!
Removing the offending line results in the usual deadlock:
Alphonse: Gaston has bowed to me!
Gaston: Alphonse has bowed to me!
... deadlock ...
Is the call to System.out.format somehow changing the way the threads acquire the intrinsic locks on the objects?
Update:
I was able to get the system to deadlock again just by changing where I start the threads in the code:
public static void main(String[] args) throws InterruptedException {
final Friend alphonse = new Friend("Alphonse");
final Friend gaston = new Friend("Gaston");
System.out.format("Hi, I'm %s...no deadlock for you!\n\n", alphonse.getName());
Thread t1 = new Thread(new Runnable() {
public void run() { alphonse.bow(gaston); }
});
Thread t2 = new Thread(new Runnable() {
public void run() { gaston.bow(alphonse); }
});
t1.start();
t2.start();
}
This begs the question about how we can get greater insight into how the thread scheduler behaves, but I’ll save that for a different day.
You did not really remove the deadlock but rather (because of some internal JVM reason) changed that timing of the threads so that one of the threads enters
bowBack()before the other callsbow().Just put in
bow:sleep(1000)and your deadlock will reappear.Note that deadlock does not always happen, only when the threads are in lucky timing. In this case, deadlock will happen when both threads enter
bowand before any of them callbowBack…
And “some internal JVM reason” can be the following:
In your case there are actually three threads: the one executing main, t1, and t2.
The reason why putting print hides the deadlock can be that the thread scheduler decided that
mainstill had work to do, i.e. flushing the io buffers, and therefore let main continue after starting t1 and before starting t2. If you are on a dual-core cpu, onlymainandt1would run, butt2would wait, sinceprintis a slow operation. Context switching would take some more time, and t1 would finish before t2 could start…so no deadlock would happen. But that does not mean that if you run the program again deadlock won’t happen.If you want to play, create a
queueand push tokens (thread names) in that queue, thenjoinyour threads in main. After they finish, print the queue content and you can observe the timing of the threads.