- 206 - Note, however, that threads synchronized on different monitors can gain entry to the same
block at any time. For example, a block defined with a
synchronizedthis
expression is synchronized on the monitor of the
this
object. If
this
is an object that is different for two different threads, both threads can gain ownership of their own monitor for that
block, and both can execute the block at the same time. This wont matter if the block affects only variables specific to its thread such as instance variables of
this
, but can lead to corrupt states if the block alters variables that are shared between the threads.
Multithreading needs more care in coding than single threading. When tuning threads, it is easy to make a little change here, and a little change there, and end up with total confusion, race conditions,
and deadlock. Before you start tuning threads, it is important to have a good understanding of how they interact and how to make them cooperate and control each other. This book is not a tutorial on
threads, so I dont intend to cover the subject from a non-performance-tuning standpoint in any great detail. Two excellent books on Java threads are Java Threads by Scott Oaks and Henry Wong
OReilly and Concurrent Programming in Java by Doug Lea Addison Wesley.
If you are not comfortable with Java synchronization and how it works, I strongly advise you to spend some time studying how to use threads and synchronization. Be sure you understand how
race conditions and deadlocks occur many articles and books on Java go into this in detail, and there are brief examples in the later sections of this chapter. Be sure you know how to correctly use
the various
wait
and
notify
methods in the
Object
class as well as the
synchronized
keyword, and understand which monitor objects are used and how they are used when execution reaches a synchronized block or method.
10.1 User-Interface Thread and Other Threads
The users impression of the performance of an application is greatly affected by its responsiveness. Putting the user interface into a separate thread from any other work makes the application feel far
more responsive to the user and ensures that an unexpectedly long operation doesnt freeze the applications screen.
This user-interface thread is quite important in applets , where it is simple to use the screen-update thread to execute other tasks since you can easily call code from the
paint
method. Although more effort is required to spawn a thread to execute other tasks, it is much better to do so, as
otherwise you can easily block repainting the screen or other GUI responses. In Figure 10-1
, the clock on the left has been resized to a quarter of its original size, but the
paint
method has been unable to resize the clock drawing, as the
paint
method is busy keeping the correct time. The clock on the right has been resized to a wide rectangular shape, and it keeps perfect time while also
responding to the resize request, because its
paint
method always completes quickly.
Figure 10-1. The effect of timing on redrawing
- 207 - If you are able to separate operations that slow processing such as IO into specialized threads,
your application will run more smoothly. It can carry on its main work while another thread anticipates the need for data, saves data to disk, etc. However, you should not pass work to another
thread while your main thread just sits and waits until that other thread completes. In fact, doing this is likely to hurt performance rather than improve it. You should not use extra threads unless you
have good design or performance reasons for doing so.
One useful technique is to use a separate thread to monitor the rest of the application and, when necessary, interrupt threads that are running beyond their expected execution time. This is more
often a technique that ensures robustness, but it can apply to performance too, when a calculation provides successively better approximations to the required result. It may be reasonable to interrupt
the calculation after a certain length of time, assuming you have a good approximation calculated. This technique does not specifically require a supervising thread, as the timeout checking could be
done within the calculation. It is often used in animation; the frame-display rate can be adjusted according to the time taken to display the frames, which in turn depends on picture resolution and
the system environment.
All in all, using multiple threads needs careful consideration, and should be planned for in the design stage. Retrofitting an application to use threads at an intermediate or advanced stage can
sometimes be done quite simply in some sections of the application, but is not usually possible throughout the application. In any case, care should be taken when changing the design to use more
threads, so that the problems illustrated in the next sections are avoided.
10.2 Race Conditions