guyr79
guyr79

Reputation: 167

question about concurrency code from oracle.com that explains Deadlock

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

Answers (4)

Dici
Dici

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:

  • Thread 1 calls Alphonse.bow
  • Thread 1 acquires Alphonse.lock
  • Thread 2 calls Gaston.bow
  • Thread 2 acquires Gaston.lock
  • Thread 1 calls Gaston.bowBack
  • Thread 1 waits for Gaston.lock to be released because Thread 2 is still owning it
  • Thread 2 calls Alphonse.bowBack
  • Thread 2 waits for Alphonse.lock to be released because Thread is still owning it

Therefore, both threads are holding a lock that the other threads need to complete, and that's a deadlock!

Upvotes: 2

RealSkeptic
RealSkeptic

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

Michał Krzywański
Michał Krzywański

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 :

  1. Thread 1 acquires instristic lock on alphonse object when calling alphonse.bow() method.
  2. Thread 2 acquires instrictic lock on gaston object when calling gaston.bow() method.
  3. Thread 1 tries to call bower.bowBack(this); where bower refers to gaston object. It will have to wait since another thread holds instrictic lock on gaston object.
  4. Thread 2 tries to call bower.bowBack(this); where bower refers to alphonse object. It will have to wait since another thread holds instrictic lock on alphonse object.
  5. Since both threads are waiting for a resource which is held by another one - we have a deadlock.

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

JB Nizet
JB Nizet

Reputation: 692071

  • Thread 1: alphonse.bow(gaston), so Thread 1 acquires the lock of alphonse to be able to call its synchronized method bow
  • Thread 2: gaston.bow(alphonse), so Thread 2 acquires the lock of gaston to be able to call its synchronized method bow
  • Thread 1: 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.
  • 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

Related Questions