Take the following example. There are two classes A
and B in an application both extend Thread (or
Runnable). Those two classes both have access to
instance variables of class C named iA and iB. Now A's
run method has the following code.
run(){
synchronized(C.iA){
// .. do some stuff
synchronized(C.iB){
// do more stuff
}
}
}
Now here is B's run.
run(){
synchronized(C.iB){
// .. do some stuff
synchronized(C.iA){
// do more stuff
}
}
}
Now for the sake of example say A's run method is
called a couple of milliseconds before B's. Now a
requests and obtains a lock on C.iA then waits to see
if it can obtain a lock on C.iB. Say in the span of
time that A was waiting to obtain a lock on C.iA B's
run method is executed hence obtaining for B a lock on
C.iB then waiting to obtain a lock on C.iA.
Can you see the problem? Both objects are waiting for
each other to release the locks on the object it needs
before they release the lock on the object they hold.
A has a lock on C.iA and is waiting to obtain a lock
on C.iB while B has it backwards. Both objects can
only release their corresponding locks after they are
able to obtain a lock on the other object but that is
impossible here as they are both waiting on each
other.