Question

public void method(Type1 inst1, Type2 inst2) {
  synchronized(inst1) {
    synchronized(inst2) {
      //do something now
    }
  }
}

I can understand from this piece of code that once a thread enters the method, it acquires the lock on inst1, and then it acquires lock on inst2, without releasing inst1 lock. I assume that both of these objects are not locked by other thread.

  1. If a thread can acquire lock on only one object at once and can only own another lock when lock on current object has been released, how can this piece of code be valid, or rather, is it a valid code that I think I have seen somewhere?
  2. And what if Type1 and Type2 are same?
  3. What happens if we make the method synchronized, considering it resides in a class other than the parameter types of the method?
Was it helpful?

Solution

1 . If a thread can acquire lock on only one object at once and can only own another lock when lock on current object has been released, how can this piece of code be valid, or rather, is it a valid code that I think I have seen somewhere?

It is a valid code, the locks are not on the object where the method resides, but on inst1 and inst2. Also, the lock is not on the class, but for every object

2 . And what if Type1 and Type2 are same?

again, the locks are on the objects, not on the classes. if inst1 and inst2 are the same, then the thread has only one lock, it is valid for the same thread to "re-enter" the lock

3 . What happens if we make the method synchronized, considering it resides in a class other than the parameter types of the method?

Then you have yet another lock, this time on the object (not the class) where the method is being executed.

OTHER TIPS

Thanks to @morgano and @Nick Holt, I understand that a thread can hold multiple locks at the same time (different objects), or on the same object multiple times (locks obtained using synchronized are implicitly reentrant).

There is no problem with your code in case every time you need to obtain lock on those two objects you do it in the same order.

For example, if you have two methods

public void methodA(Type1 inst1, Type2 inst2) {
  synchronized(inst1) {
    synchronized(inst2) {
      //do something now
    }
  }
}

public void methodB(Type1 inst1, Type2 inst2) {
  //wrong
  synchronized(inst2) {
    synchronized(inst1) {
      //do something now
    }
  }
}

You have a potential deadlock problem as a thread could obtain the lock on inst1 and wait for the lock of inst2, while another thread have the lock on inst2 and wait for lock on inst1.

That code is very likely to result in a deadlock minefield, for sure.

The 2nd thread(threa2) that locks inst2 does not necessarily have to be running through methodA, but may lock object inst2 in another procedure, and does so before the thread1 in methodA, and then accesses method A and attempts to lock inst2, before releasing inst2. ahhhhh! boooom! :(.

suggested solution :
synchronize on methodA, and not on the objects , as that will lock all resources accessed in methodA() like so

public synchronized void methodA () {....}

Deadlocks happen when you acquire one lock inside another:

lock OB1 {
   lock OB2 { ... }
}    

because one could attempt to lock on a pair (1,2) and anther (2,1). There are possible more lengthy chains: (1,2) + (2,3) + (3,1).

You need to introduce order between locks to avoid deadlocks: Java synchronisation: atomically moving money across account pairs?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top