It
is vital to learn when to use multithreading
and when to avoid it. The main reason to use it is to manage a number of tasks
whose intermingling will make more efficient use of the computer or be more
convenient for the user. The classic example of resource balancing is using the
CPU during I/O waits. The classic example of user convenience is monitoring a
“stop” button during long downloads.
The
main drawbacks to multithreading are:
Slowdown
while waiting for shared resources
Additional
CPU overhead required to manage threads
Unrewarded
complexity, such as the silly idea of having a separate thread to update each
element of an array
Pathologies
including starving, racing, and deadlock
An
additional advantage to threads is that they substitute “light”
execution context switches (of the order of 100 instructions) for
“heavy” process context switches (of the order of 1000s of
instructions). Since all threads in a given process share the same memory
space, a light context switch changes only program execution and local
variables. On the other hand, a process change, the heavy context switch, must
exchange the full memory space.
Threading
is like stepping into an entirely new world and learning a whole new
programming language, or at least a new set of language concepts. With the
appearance of thread support in most microcomputer operating systems,
extensions for threads have also been appearing in programming languages or
libraries. In all cases, thread programming (1) seems mysterious and requires a
shift in the way you think about programming and (2) looks similar to thread
support in other languages, so when you understand threads, you understand a
common tongue. And although support for threads can make Java seem like a more
complicated language, don’t blame Java. Threads are tricky.
One
of the biggest difficulties with threads occurs because more than one thread
might be sharing a resource, such as the memory in an object, and you must make
sure that multiple threads don’t try to read and change that resource at
the same time. This requires judicious use of the
synchronized
keyword, which is a helpful tool but must be understood thoroughly because it
can quietly introduce deadlock situations.
In
addition, there’s a certain art to the application of threads. Java is
designed to allow you to create as many objects as you need to solve your
problem – at least in theory. (Creating millions of objects for an
engineering finite-element analysis, for example, might not be practical in
Java.) However, it seems that there is an upper bound to the number of threads
you’ll want to create because at some point a large number of threads
seems to become unwieldy. This critical point is not in the many thousands as
it might be with objects, but rather in the neighborhood of less than 100. As
you often create only a handful of threads to solve a problem, this is
typically not much of a limit, yet in a more general design it becomes a
constraint.
A
significant non-intuitive issue in threading is that, because of thread
scheduling, you can typically make your applications run
faster
by inserting calls to
sleep( )
inside
run( )’s
main loop. This definitely makes it feel like an art, in particular when the
longer delays seem to speed up performance. Of course, the reason this happens
is that shorter delays can cause the end-of-
sleep( )
scheduler interrupt to happen before the running thread is ready to go to
sleep, forcing the scheduler to stop it and restart it later so it can finish
what it was doing and then go to sleep. It takes extra thought to realize how
messy things can get.
One
thing you might notice missing in this chapter is an animation example, which
is one of the most popular things to do with applets. However, a complete
solution (with sound) to this problem comes with the Java JDK (available at
java.sun.com)
in the demo section. In addition, we can expect better animation support to
become part of future versions of Java, while completely different non-Java,
non-programming solutions to animation for the Web are appearing that will
probably be superior to traditional approaches. For explanations about how Java
animation works, see
Core
Java
by Cornell & Horstmann, Prentice-Hall 1997. For more advanced discussions
of threading, see
Concurrent
Programming in Java
by Doug Lea, Addison-Wesley 1997, or
Java
Threads
by Oaks & Wong, O’Reilly 1997.