Chapter 10. Threading
Minor Premise: One man can dig a posthole in sixty
seconds. Conclusion: Sixty men can dig a posthole in one second. —Ambrose Bierce, The Devil's Dictionary
Multithreading allows an
application to do multiple things at the same time. While it is often
possible to get the same effect with clever programming in a single
thread, Java's extensive support of threads makes it
easier to use multiple threads. In addition, single-threaded
applications cannot take advantage of multiprocessor machines.
However, multithreading can be difficult to implement effectively.
Multithreading improves performance in many cases, but it also has
drawbacks if the default mechanisms for cooperation between threads
are used simplistically. In this chapter, we look at the benefits and
the disadvantages threads offer to performance. We examine the likely
problems that may be encountered and discuss how to minimize the
performance downside while still gaining the benefits of multiple
threads.
Synchronization can be confusing, so I felt
it was worth including a short reminder of its subtleties here.
Two or more threads accessing and updating the same data variables
have no way of knowing when a particular
accessor
update will occur relative to any other thread accesses.
Synchronization ensures that a group of statements (a synchronized
block) will execute atomically as far as all synchronized threads are
concerned. Synchronization does not address the problem of which
thread executes the statements first: it is first come, first served.
Synchronization is achieved using monitors. Every object can have a
monitor associated with it, so any object can synchronize blocks.
Before a synchronized block can be entered, a thread needs to gain
ownership of the monitor for that block. Once the thread has gained
ownership of the monitor, no other thread synchronized on the same
monitor can gain entry to that block (or any other block or method
synchronized on the same monitor). The thread owning the monitor gets
to execute all the statements in the block, and then automatically
releases ownership of the monitor on exiting the block. At that
point, another thread waiting to enter the block can acquire
ownership of the monitor.
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 synchronized(this) 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 won't 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, such as static
variables.
|
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 don't 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 (O'Reilly) 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.
|