Contextual Ads
More Java Resources
Advertisement
Among the defining characteristics of Java is its built-in support for multithreaded
programming. This support, which has been present in Java from the start, is
provided by the Thread class, the Runnable interface, several methods supplied
by Object, and the synchronized keyword. Multithreading enables you to write programs
that contain two or more separate paths of execution that can execute concurrently. Each
path of execution is called a thread. Through the careful use of multithreading, you can
create programs that make efficient use of system resources and maintain a responsive
user interface.
Because multiple threads can interact in ways that are not always intuitive, adding
level of complexity that is not present in a single-threaded program, some programmers
avoid multithreading whenever possible. However, the modern programming world is
moving toward more use of multithreading, not less. Highly parallel architectures are
becoming the norm. Simply put, multithreading will continue to play a critical part in many
(perhaps most) real-world applications of Java.
Multithreading Fundamentals
At its core, multithreading is a form of multitasking. There are two distinct types of
multitasking: process-based and thread-based. It is important to differentiate between the two.
As it relates to this discussion, a process is, in essence, a program that is executing. Thus,
process-based multitasking is the feature that allows your computer to run two or more
programs concurrently. For example, it is process-based multitasking that allows you to
download a file at the same time you are compiling a program or sorting a database. In
process-based multitasking, a program is the smallest unit of code that can be dispatched by
the scheduler.
In a thread-based multitasking environment, the thread is the smallest unit of dispatchable
code. Because a program can contain more than one thread, a single program can use multiple
threads to perform two or more tasks at once. For instance, a browser can begin rendering a
Web page while it is still downloading the remainder of the page. This is possible because
each action is performed by a separate thread. Although Java programs make use of processbased
multitasking environments, process-based multitasking is not under the direct control
of Java. Multithreaded multitasking is.
All processes have at least one thread of execution, which is called the main thread,
because it is the one that is executed when a program begins. From the main thread, you
can create other threads. These other threads can also create threads, and so on.
Multithreading is important to Java for two main reasons. First, multithreading enables
you to write very efficient programs because it lets you utilize the idle time that is present
in most programs. Most I/O devices, whether they be network ports, disk drives, or the
keyboard, are much slower than the CPU. Thus, a program will often spend a majority of
its execution time waiting to send or receive information to or from a device. By using
multithreading, your program can execute another task during this idle time. For example,
while one part of your program is sending a file over the Internet, another part can be
handling user interaction (such as mouse clicks or button presses), and still another can be
buffering the next block of data to send.
The second reason that multithreading is important to Java relates to Java’s eventhandling
model. A program (such as an applet) must respond quickly to an event and then
return. An event handler must not retain control of the CPU for an extended period of time.
If it does, other events will not be handled in a timely fashion. This will make an application
appear sluggish. It is also possible that an event will be missed. Therefore, if an event
requires some extended action, then it must be performed by a separate thread.
A thread can be in one of several states. It can be running. It can be ready to run as soon
as it gets CPU time. A running thread can be suspended, which is a temporary halt to its
execution. It can later be resumed. A thread can be blocked when waiting for a resource. A
thread can be terminated, in which case its execution ends and cannot be resumed.
Along with thread-based multitasking comes the need for synchronization, which allows
the execution of threads to be coordinated in certain well-defined ways. Java has extensive,
integrated support for synchronization, which is achieved through the use of a monitor,
which all objects have, and the synchronized keyword. Thus, all objects can be
synchronized.
Two or more threads can communicate with each other through methods that are defined
by Object. These methods are wait( ), notify( ), and notifyAll( ). They enable one thread to
wait on another. For example, if one thread is using a shared resource, then another thread
must wait until the first thread has finished. The waiting thread can resume execution when
the first thread notifies it that the resource is now available.
There are two basic types of threads: user and daemon. A user thread is the type of thread
created by default. For example, the main thread is a user thread. In general, a program
continues to execute as long as there is at least one active user thread. It is possible to change
the status of a thread to daemon. Daemon threads are automatically terminated when all nondaemon
threads have terminated. Thus, they are subordinate to user threads.
Threads can be part of a group. A thread group enables you to manage related threads
collectively. For example, you can obtain an array of the threads in the group.
Java’s multithreading system is built upon the Thread class and its companion interface,
Runnable. Thread encapsulates a thread of execution. To create a new thread, your program
will either implement the Runnable interface or extend Thread. Both Runnable and Thread
are packaged in java.lang. Thus, they are automatically available to all programs.
The Runnable Interface
The java.lang.Runnable interface abstracts a unit of executable code. You can construct a
thread on any object that implements the Runnable interface. Therefore, any class that you
intend to run in a separate thread must implement Runnable. Runnable defines only one method called run( ), which is declared like this:
Inside run( ), you will define the code that constitutes the new thread. It is important to
understand that run( ) can call other methods, use other classes, and declare variables just
like the main thread. The only difference is that run( ) establishes the entry point for
another, concurrent thread of execution within your program. This thread will end when
run( ) returns.
Once you have created an instance of a class that implements Runnable, you create a
thread by constructing an object of type Thread, passing in the Runnable instance. To start
the thread running, you will call start( ) on the Thread object, as described in the next
section.
The Thread Class
The Thread class encapsulates a thread. It is packaged in java.lang and implements the
Runnable interface. Therefore, a second way to create a thread is to extend Thread and
override the run( ) method. Thread also defines several methods that help manage threads.
Here are the ones used in this chapter:
The Methods Defiend by InputStream
| Method |
Description |
| static Thread currentThread( ) |
Returns a reference to a Thread object that represents
the invoking thread. |
| long getID( ) |
Returns a thread’s ID. |
| final String getName( ) |
Obtains a thread’s name. |
| final int getPriority( ) |
Obtains a thread’s priority. |
| Thread.State getState( ) |
Returns the current state of the thread. |
| static boolean holdsLock(Object obj) |
Returns true if the invoking thread holds the lock on obj. |
| void interrupt( ) |
Interrupts a thread. |
| static boolean interrupted( ) |
Returns true if the invoking thread has been
interrupted. |
| final boolean isAlive( ) |
Determines whether a thread is still running. |
| final boolean isDaemon( ) |
Returns true if the invoking thread is a daemon thread. |
| boolean isInterrupted( ) |
Returns true if the thread on which it is called has been
interrupted. |
| final void join( ) |
Waits for a thread to terminate. |
| void run( ) |
Entry point for the thread. |
| final void setDaemon(boolean how) |
If how is true, the invoking thread is set to daemon
status. |
| final void setName(String thrdName) |
Sets a thread’s name to thrdName. |
| final void setPriority(int level) |
Sets a thread’s priority to level. |
| static void sleep(long milliseconds) |
Suspends a thread for a specified period of
milliseconds. |
| void start( ) |
Starts a thread by calling its run( ) method. |
| static void yield( ) |
Yields the CPU to another thread. |
Pay special attention to the start( ) method. After an instance of Thread has been
created, call start( ) to begin execution of the thread. The start( ) method calls run( ), which
is the method defined by Runnable that contains the code to be executed in the thread. This
process is described in detail in the following recipes.
Another method of special interest is sleep( ). It suspends execution of a thread for a
specified period of time. When a thread sleeps, another thread can execute until the sleeping
thread awakes and resumes execution. Several examples in this chapter use sleep( ) to
demonstrate the effects of multiple threads.
Thread defines two sets of constructors, one for constructing a thread on a separate
instance of Runnable and the other for constructing a thread on classes that extend Thread.
Here are the constructors that take a separate instance of Runnable:
Here, thrdObj is a reference to an instance of a class that implements Runnable. This object’s
run( ) method contains the code that will be executed as the new thread. The name of thread
is passed in thrdName. If no name is specified (or the name is null), then a name is supplied
automatically by the JVM. The thread group to which the thread belongs (if any) is passed
via thrdGroup. If the thread group is not specified, then the thread group is determined by
the security manager (if there is one) or is set to the same group as the invoking thread.
Here are the constructors that create a thread for classes that extend Thread:
The first constructor creates a thread that uses the default name and thread group, as
described already. The second lets you specify the name. The third lets you specify the
thread group and the name.
For both sets of constructors, the thread will be created as a user thread unless the creating
thread is a daemon thread. In this case, the thread will be created as a daemon thread.
There is another Thread constructor that lets you specify a stack size for the thread.
However, because of differences in execution environments, the API documentation states
that "extreme care should be exercised in its use." |