Java Reference
In-Depth Information
The sequence of events is as follows:
thread1
starts first, and synchronizes on
theObject
. This prevents any methods for
theObject
being called by any other thread.
❑
thread1
then calls
sleep()
so
thread2
can start.
❑
thread2
starts and synchronizes on
theOtherObject
. This prevents any methods for
theOtherObject
being called by any other thread.
❑
thread2
then calls
sleep()
allowing
thread1
another go.
❑
thread1
wakes up and tries to call
method2()
for
theOtherObject
, but it can't until the
code block in
thread2
that is synchronized on
theOtherObject
completes execution.
❑
thread2
gets another go because
thread1
can't proceed, and tries to call
method1()
for
theObject
. This can't proceed until the code block in
thread1
that is synchronized on
theObject
completes execution.
❑
Neither thread has any possibility of continuing - they are deadlocked. Finding and fixing this sort of
problem can be very difficult, particularly if your program is complicated and has other threads that will
continue to execute.
You can create a trivial deadlock in the last example by making the
for
loop in
main()
synchronized
on one of the accounts. For example:
synchronized(accounts[1]) {
for(int i = 1; i <= transactionCount; i++) {
// code for generating transactions etc...
}
}
A deadlock occurs as soon as a transaction for
accounts[1]
arises because the
doTransaction()
method in the
theBank
object that is called by a
Clerk
object to handle the transaction will be
synchronized to the same object and can't execute until the loop ends. Of course, the loop can't
continue until the method in the
theBank
object terminates so the program hangs.
In general, ensuring that your program has no potential deadlocks is extremely difficult. If you intend to
do a significant amount of programming using threads, you will need to study the subject in much more
depth than we can deal with here. A good book on the subject is
Concurrent Programming in Java: Design
Principles and Patterns
written by Doug Lea (ISBN 0-201-69581-2).
Communicating between Threads
We've seen how we can lock methods or code blocks using synchronization to avoid the problems that
uncontrolled thread execution can cause. While this gives us a degree of control, we're still introducing
inefficiencies into the program. In the last example, there were several occasions where we used a loop
to wait for a clerk thread to complete an operation before the current thread could sensibly continue.
For example, we couldn't pass a transaction to a
Clerk
object while that object was still busy with the
previous transaction. Our solution to this was to use a
while
loop to test the busy status of the
Clerk
object from time to time and call the
sleep()
method in between. But there's a much better way.