Reputation: 167
I got a cross a piece of code from oracle.com which explains concurrency in general and deadlock in particular. the code is from :
https://docs.oracle.com/javase/tutorial/essential/concurrency/deadlock.html
I tried hard to understand what is going on and what causes the deadlock here through debugging and manually running the code, but for no avail.
I would be very thankful if someone could break the flow of this code and explain why and where the code gets blocked (which it does - I ran it on my machine).
Thank you very much
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) {
final Friend alphonse =
new Friend("Alphonse");
final Friend gaston =
new Friend("Gaston");
new Thread(new Runnable() {
public void run() { alphonse.bow(gaston); }
}).start();
new Thread(new Runnable() {
public void run() { gaston.bow(alphonse); }
}).start();
}
}
when running it - I get the following output:
Alphonse: Gaston has bowed to me!
Gaston: Alphonse has bowed to me!
Upvotes: 1
Views: 107
Reputation: 25980
The synchronized
keyword implicitly means a lock will be acquired upon entering the method or block. When it is used on a method, it is equivalent to this:
public void bow(Friend bower) {
synchronized (this) {
System.out.format("%s: %s has bowed to me!%n", this.name, bower.getName());
bower.bowBack(this);
}
}
Only one thread at a time can acquire this lock, meaning that only one thread can run either bow
or bowBack
at a time since the lock is held by this
.
Now with that in mind, here's one possible deadlock scenario in detail:
Alphonse.bow
Alphonse.lock
Gaston.bow
Gaston.lock
Gaston.bowBack
Gaston.lock
to be released because Thread 2 is still owning itAlphonse.bowBack
Alphonse.lock
to be released because Thread is still owning itTherefore, both threads are holding a lock that the other threads need to complete, and that's a deadlock!
Upvotes: 2
Reputation: 34638
You have two threads. Thread A
is told to run alphonse.bow(gastone)
, and thread G
is told to run gaston.bow(alphonse)
.
Upon entering the bow
, each thread acquires a lock. Thread A
acquires alphonse
's lock, while thread G
acquirels gaston
's lock.
Let's look at thread A
. It acquired alphonse
's lock, performed the bow, and now
it tries to run gastone.bowBack(alphonse)
. To do that, it needs to acquire gastone
's lock.
However, gastone
's lock is held by thread G
, which is doing the same thing - trying to acquire alphonse
's lock for a bow back.
The problem here is that the threads attempt to do this while still holding on to the original lock they acquired. Thread A
is holding alphonse
's lock, and has not released it, and is now trying to get gastone
's lock. It cannot be released, because thread G
is holding it while waiting for thread A
to release its hold on alphonse
's lock.
The point here is that if you call another method while you are in a synchronized method, you're not releasing the locks you already have.
Upvotes: 1
Reputation: 16920
The problem is that there are two objects and also that a synchronized
method bow
calls another synchronized method bowBack
on an object that was passed as an argument.
So what happens there :
alphonse
object when calling alphonse.bow()
method.gaston
object when calling gaston.bow()
method.bower.bowBack(this);
where bower
refers to gaston
object. It will have to wait since another thread holds instrictic lock on gaston
object.bower.bowBack(this);
where bower
refers to alphonse
object. It will have to wait since another thread holds instrictic lock on alphonse
object.Of course when you try to run your program the deadlock might not appear. But there is a great possibility of it to appear so it should be eliminated.
Upvotes: 1
Reputation: 692071
alphonse.bow(gaston)
, so Thread 1 acquires the lock of alphonse to be able to call its synchronized method bow
gaston.bow(alphonse)
, so Thread 2 acquires the lock of gaston to be able to call its synchronized method bow
bower.bowBack(this);
, i.e. gaston.bowBack(alphonse)
, so thread 1 needs to acquire the lock of gaston to be able to call its synchronized method bowBack
. But thread 2 already has this lock, so thread 1 blocks, waiting until the lock is released by thread 2.bower.bowBack(this);
, i.e. alphonse.bowBack(gaston)
, so thread 2 needs to acquire the lock of alphonse to be able to call its synchronized method bowBack
. But thread 1 already has this lock, so thread 2 blocks, waiting until the lock is released by thread 1.You thus have a deadlock: both threads are waiting forever until the other one release a lock.
Upvotes: 1