Java Reference
In-Depth Information
Software Engineering Observation 23.1
Java provides higher-level concurrency utilities to hide much of this complexity and make
multithreaded programming less error prone. Thread priorities are used behind the scenes
to interact with the operating system, but most programmers who use Java multithreading
will not be concerned with setting and adjusting thread priorities.
Portability Tip 23.1
Thread scheduling is platform dependent—the behavior of a multithreaded program
could vary across different Java implementations.
When a higher-priority thread enters the
ready
state, the operating system generally preempts
the currently
running
thread (an operation known as
preemptive scheduling
). Depending
on the operating system, a steady influx of higher-priority threads could postpone—possibly
indefinitely—the execution of lower-priority threads. Such
indefinite postponement
is
sometimes referred to more colorfully as
starvation
. Operating systems employ a technique
called
aging
to prevent starvation—as a thread waits in the
ready
state, the operating system
gradually increases the thread's priority to ensure that the thread will eventually run.
Another problem related to indefinite postponement is called
deadlock
. This occurs
when a waiting thread (let's call this thread1) cannot proceed because it's waiting (either
directly or indirectly) for another thread (let's call this thread2) to proceed, while simulta-
neously thread2 cannot proceed because it's waiting (either directly or indirectly) for
thread1 to proceed. The two threads are waiting for each other, so the actions that would
enable each thread to continue execution can never occur.
Framework
This section demonstrates how to perform concurrent tasks in an application by using
Executors
and
Runnable
objects.
Creating Concurrent Tasks with the
Runnable
Interface
You implement the
Runnable
interface (of package
java.lang
) to specify a task that can
execute concurrently with other tasks. The
Runnable
interface declares the single method
run
, which contains the code that defines the task that a
Runnable
object should perform.
Executing
Runnable
Objects with an
Executor
To allow a
Runnable
to perform its task, you must execute it. An
Executor
object executes
Runnable
s. It does this by creating and managing a group of threads called a
thread pool
.
When an
Executor
begins executing a
Runnable
, the
Executor
calls the
Runnable
object's
run
method, which executes in the new thread.
The
Executor
interface declares a single method named
execute
which accepts a
Run-
nable
as an argument. The
Executor
assigns every
Runnable
passed to its
execute
method
to one of the available threads in the thread pool. If there are no available threads, the
Executor
creates a new thread or waits for a thread to become available and assigns that
thread the
Runnable
that was passed to method
execute
.