Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java deadlock question

Tags:

java

can anyone explain me why there is a deadlock in this code.Thanks

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();
    }
}
like image 876
Jony Avatar asked Aug 11 '10 04:08

Jony


People also ask

How do you resolve a deadlock in Java?

We can avoid Deadlock situation in the following ways: Using Thread. join() Method: We can get a deadlock if two threads are waiting for each other to finish indefinitely using thread join. Then our thread has to wait for another thread to finish, it is always best to use Thread.

What is deadlock in Java with example?

Deadlock in Java is a condition where two or more threads are blocked forever, waiting for each other. This usually happens when multiple threads need the same locks but obtain them in different orders. Multithreaded Programming in Java suffers from the deadlock situation because of the synchronized keyword.

How deadlock is detected in Java?

There is one more method to detect Deadlock in Java, it can be done by running the program in CMD. All we need to do is collect thread dumps and then we have to command to collect, depending upon the operating system. If we are running Java 8 on windows, a command would be jcmd $PID Thread.


1 Answers

Consider the following:

  • Let Thread1 run() { alphonse.bow(gaston); }
  • Let Thread2 run() { gaston.bow(alphonse); }
  • Thread1 enters alphonse.bow(gaston);, locking alphonse since bow() is synchronized
  • Thread2 enters gaston.bow(alphonse);, locking gaston since bow() is synchronized
  • In Thread1, bower.bowBack(this); evaluates to gaston.bowBack(alphonse);
    • Thread1 attempts to obtain the lock for gaston, currently held by Thread2
  • In Thread2, bower.bowBack(this); evaluates to alphonse.bowBack(gaston);
    • Thread2 attempts to obtain the lock for alphonse, currently held by Thread1
  • each thread is waiting for the other to release a lock, hence deadlock

The problem is that there is excessive synchronized currently. There are many ways to "fix" this; here's an instructive solution:

    public void bow(Friend bower) {
        synchronized (this) {
            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());
    }

Now bowBack() is fully synchronized, but bow() is only synchronized partially, using the synchronized(this) statement. This will prevent the deadlock.

Here are quotes from Effective Java 2nd Edition, Item 67: Avoid excessive synchronization

To avoid liveness and safety failures, never cede control to the client within a synchronized method or block. In other words, inside a synchronized region, do not invoke a method designed to be overridden, or provided by a client in the form of a function object. From the perspective of the class with the synchronized region, such methods are alien. The class has no knowledge of what the method does and have no control over it. Depending on what an alien method does, calling it from a synchronized region can cause exceptions, deadlocks, or data corruption.

[...] As a rule, you should do as little work as possible inside synchronized regions. Obtain the lock, examine the shared data, transform it if necessary, and drop the lock.

In essence, bower.bowBack(this) is an attempt to cede control to an alien method, because bowBack() is not a final method in class Friend. Consider the following attempt to fix the problem, for example:

    // attempt to fix: STILL BROKEN!!!

    public synchronized void bow(Friend bower) {
        System.out.format("%s: %s has bowed to me!%n", 
            this.name, bower.getName());
        bower.bowBack(this);
        // ceding control to alien method within synchronized block!
    }
    
    // not a final method, subclasses may @Override
    public void bowBack(Friend bower) {
        System.out.format("%s: %s has bowed back to me!%n",
                this.name, bower.getName());
    }

The above code will not deadlock with the current alphonse/gaston scenario, but since bow() cedes control to a non-final method bowBack(), a subclass can @Override the method in such a way that will cause bow() to deadlock. That is, bowBack() is an alien method to bow(), and thus should NOT have been invoked from within a synchronized region.

References

  • JLS 8.4.3.6 synchronized Methods
  • JLS 14.19 The synchronized Statement
  • JLS 17.1 Locks

See also

  • Effective Java 2nd Edition
    • Item 66: Synchronize access to shared mutable data
    • Item 15: Minimize mutability
like image 68
polygenelubricants Avatar answered Oct 03 '22 16:10

polygenelubricants