7
public void function(object a, object b){
    synchronized(a){
        synchronized (b){
           a.performAction(b);
           b.performAction(a);
        }
    }
}

Deadlock with 2 Threads? Thanks for the answers!

Radiodef
  • 37,180
  • 14
  • 90
  • 125
MrBoolean
  • 75
  • 4
  • No, both threads will wait for 'a'. It could be if there was second method where you first synchronize on 'b' and then on 'a'. – Stan May 25 '15 at 19:57
  • So if performAction() would use the object a in a synchronized method there would be a Deadlock for instance? – MrBoolean May 25 '15 at 20:04
  • In provided case no since thread 1 will wait for release of object 'a' until method end – Stan May 25 '15 at 20:06
  • 2
    Actually I think if 2 Threads execute funktion with the Objects thread1 (a,b) and thread 2 (b, a) at the same time there is a deadlock because both are waiting for an object in the 2nd synchronize or am I wrong? – MrBoolean May 25 '15 at 20:14
  • thread1 (a,b) and thread 2 (b, a) - in this case yes, it's possible – Stan May 25 '15 at 20:15
  • http://stackoverflow.com/questions/4604003/synchronized-block-lock-more-than-one-object look the answers here, one of them is the same as your question. – maraca May 25 '15 at 20:24
  • What do you want to be synchronized? I need more context. This piece of code is actually a piece of bad design imho. To prevent deadlock you could write `public synchronized void function(...)`. You didn't ask for best performance. Some people don't like Java because sometimes it's more to write. You cannot write those two `synchronized` blocks in a shorter way, but two nested blocks are nearly never a good choice. – Aitch May 25 '15 at 20:46

2 Answers2

11

Sure,

Suppose we have two objects,

Object one = ...;
Object two = ...;

And suppose thread 1 calls:

function(one, two);

While thread 2 calls:

function(two, one);

In thread 1, a == one and b == two, but in thread 2, a == two and b == one.

So while thread 1 is obtaining a lock on object one, thread 2 can be obtaining the lock on object two. Then when each of the threads tries to take the next step, they will be deadlocked.

Solomon Slow
  • 25,130
  • 5
  • 37
  • 57
2

To avoid the problem stated by jame's answer, you need to create a single lock to hold both objects, no matter the order they are passed to the function:

public class TwoObjectsLock {
    private Object a;
    private Object b;

    public TwoObjectsLock(Object a, Object b){
        this.a = a;
        this.b = b;
    }

    @Override
    public void equals(Object obj){
        if (this == obj) return true;
        if (obj instanceof TwoObjectsLock){
            TwoObjectsLock that = (TwoObjectsLock) obj;
            return (this.a.equals(that.a) && this.b.equals(that.b)) ||
                   (this.a.equals(that.b) && this.b.equals(that.a));
        }
        return false;
    }

    @Override
    public int hashCode(){
        return a.hashCode() + b.hashCode();
    }
}

And in your function you need to store the lock somehow:

private final Map<TwoObjectsLock, TwoObjectsLock> lockInstances = new HashMap<>();

public void function(Object a, Object b){
    TwoObjectsLock lock = new TwoObjectsLock(a,b);
    synchronized(lockInstances){
        TwoObjectsLock otherLock = lockInstances.get(lock);
        if (otherLock == null){
            lockInstances.put(lock, lock);
        }
        else {
            lock = otherLock;
        }
    }

    synchronized(lock){
       a.performAction(b);
       b.performAction(a);
    }
}

Not optimal but can work.

Gilberto Torrezan
  • 5,113
  • 4
  • 31
  • 50