Understanding the thread subjects discussed in this chapter will
allow you to use threads for most of your basic multiprocessing
tasks. However, thread programming involves many other topics and
there are whole books on thread programming.
We briefly review some additional items of interest here:
- join()
- this method in the Thread
class allows one thread to wait for another thread to complete
its task before it proceeds further. This provides a cruder form
of synchronisation than the wait(),
notify() methods. The join(long
millis), join(long
millis, int nanos) methods wait on the thread for the specified
times before continuing.
- ThreadLocal
- When you deal with multiple instances of a thread you may want
to give each a unique identification name or number. You could
use an instance variable for this but an alternative is to use
a single instance of ThreadLocal.
Each thread can call the set(Object
id) method in ThreadLocal
and pass an identification parameter (e.g. a string name or
Integer object). Then when the get()
method for the ThreadLocal
object is invoked, it will recognized which thread is calling
it and return the ID object.
An alternative to set()
is to create a subclass of ThreadLocal
that overrides the initialValue()
method. This method should create a new value each time it is
invoked. Then when each thread invokes this method, a unique ID
value will be created for that thread and that value will be returned
by the get()
method. See the ThreadLocal
entry in the class specifications for an example of such a subclass.
- Daemon threads - the setDaemon(boolean
on) method will mark a thread such that if it is the only
thread left running the JVM wil exit.
- ThreadGroup
- You can include thread in a set of threads by adding it to an
instance of ThreadGroup.
For example,
ThreadGroup myBunchOfThreads = new ThreadGroup("MyThreads");
creates a group with the name MyThreads.
The Thread
class has several constructors with a ThreadGroup
parameter. For example,
Thread
thread1 = new Thread(myBunchOfThreads, "Thread 1");
adds thread1
to the myBunchOfThreads
group and also gives the thread the name "Thread 1"
.
A ThreadGroup
can also hold other ThreadGroup
objects.
Grouping is useful both in several ways. For example, threads
performing a similar activity, e.g. I/O, are collected into a
neat logical set that helps with program organization. The ThreadGroup
class has several useful methods such as activeCount()
that returns the number of live threads currently in the group
and setMaxPriority(int
pri) that sets the maximum priority that any thread in
the group can have. Also, setDaemon(boolean
daemon) sets the daemon statu for the group as a whole
(i.e. a daemon ThreadGroup
dies when its last thread dies.)
- Timers
- an alternative to threads for animation and other tasks where
a regular time signal is needed is to use the timer classes. See
the Chapter 8: Tech
section for a discussion of timers and several demonstration programs.
- New concurrency
features - Java Release 5.0 adds numerous enhancements
to the threading control and concurrency features of Java. Some
of the enhancements are advanced features beyond the scope of
this course, and others require an understanding of the new generics
feature of 5.0. So we defer discussion of these until after we
have explained generics
in Chapter 10.
Thread
Resources & Peformance
Some applications require large numbers of threads such as in the
client/server systems that we will discuss in Part II. However,
you cannot create an arbitarily large number of threads at one time.
The maximum number of threads is dependent on the stack space needed
per thread and the amount of memory available. The stack size default
is 400Kbytes. For a 1 Gbyte address space this would allow up to
2500 threads but in practice the out of memory exception
would usually occur far below this.
If lots of threads are needed but individual threads have short
lifespans, then thread pooling is one option to avoid continually
making new threads. A thread in the pool is not allowed to die,
i.e. not allowed to return from run(),
when its task is completed. Instead it is returned to a queue so
that when a new thread is required, one is taken from the queue
and its parameters reset just as if it were a new thread.
The synchronization proecess,
i.e. the time it takes for a thread to obtain the lock on an object,
can take time. This has improved with the newer JVMs but in general
it's best to avoid synchronization except where it is really needed.
The Swing GUI avoids synchronization problems by requiring that
almost all modifications to the Swing components take place via
a single "super-thread". Requests for changes go on the
"main event queue" and so changes occur sequentially.
Latest update: Nov. 18, 2004
|