Wednesday, June 27, 2007

Chapter 8 - Threads

Chapter 8: Threads

Objectives

This chapter helps you to prepare for the exam by covering the following objectives:

Know what multithreading is and how it is implemented in Java.

.
Java is a multithreaded language. You must know what multithreading is in order to develop
programs and applets that take advantage of Java's multithreading capabilities. You also
need to know this in order to answer exam questions related to this topic.
Know how threads are created and executed. Write code to define instantiate and start new
threads using both java.lang.Thread and java.lang.Runnable.

.
Several exam questions require you to know the different ways threads are created, how
they are executed, and the methods they implement. You may be required to examine
examples of program code and identify specific points within the code where threads are
created and executed.
Know when and how thread scheduling takes place.

.
Scheduling enables threads to share the processing resources of one or more CPUs.
Knowledge of when and how threads are scheduled is required to evaluate the behavior of
code segments that appear in some exam questions.
Recognize conditions that might prevent a thread from executing.

.
Executing threads may perform actions that result in them entering a wait state. Other
events may also cause a thread to wait. Some threads may be prevented from reentering a
running state. Exam questions will test your knowledge of these topics.
Know how threads are synchronized. Write code using synchronizedwait(), notify(), and
notifyAll() to protect against concurrent access problems and to communicate between
threads. Define the interaction between threads and between threads and object locks when
executing synchronizedwait(), notify(), or notifyAll().

.
Synchronization allows threads to share information in a controlled manner. Without
synchronization, multithreading would be dangerous at best. Several exam questions require
you to know how synchronization is accomplished through object locks.
Study Strategies

As you read through this chapter, you should concentrate on the following key items:

.
How multithreading works
- 122



.
How threads are created
.
How threads are executed
.
How threads are scheduled
.
What methods are used to control thread states
.
How synchronization works
Chapter Introduction

This chapter covers the very important topic of Java threads. It describes how multithreading works and
how Java supports multithreading, and explains thread-related topics, such as scheduling and
synchronization. The concepts and techniques described in this chapter are important—you'll see
questions that will test your understanding of threads on the certification exam. This chapter will provide
you with the background you need to understand how threads work. The review questions and exam
questions at the end of this chapter will test your understanding and will give you an idea of the types of
questions that you may see on the exam. The concepts presented in this chapter can be complicated,
especially if you are new to multithreading. However, by studying the material and questions presented
in this chapter, you should be able to prepare for the thread-related questions that you'll see on the
certification exam.

How Multithreading Works

Multithreaded programs support more than one concurrent thread of execution—that is, they are able to
simultaneously execute multiple sequences of instructions. Each instruction sequence has its own
unique flow of control that is independent of all others. These independently executed instruction
sequences are known as threads.

If your computer has only a single CPU, you might be wondering how it can execute more than one
thread at the same time. In single-processor systems, only a single thread of execution occurs at a
given instant. The CPU quickly switches back and forth between several threads to create the illusion
that the threads are executing at the same time. Single-processor systems support logical concurrency,
not physical concurrency. Logical concurrency is the characteristic exhibited when multiple threads
execute with separate, independent flows of control. On multiprocessor systems, several threads do, in
fact, execute at the same time, and physical concurrency is achieved. The important feature of
multithreaded programs is that they support logical concurrency, not that physical concurrency is
actually achieved.
Many programming languages support multiprogramming. Multiprogramming is the logically concurrent
execution of multiple programs. For example, a program can request that the operating system execute
programs A, B, and C by having it spawn a separate process for each program. These programs can
run in a parallel manner, depending upon the multiprogramming features supported by the underlying
operating system. Multithreading differs from multiprogramming in that multithreading provides
concurrency within the context of a single process, but multiprogramming also provides concurrency
between processes. Threads are not complete processes in and of themselves. They are a separate
flow of control that occurs within a process. Figure 8.1 illustrates the difference between multithreading
and multiprogramming.

- 123



Figure 8.1: Multithreading versus multiprogramming.
Note Processes A process is the operating system object that is created when a
program is executed.

The advantage of multithreading is that concurrency can be used within a process to implement multiple
instances of simultaneous services. Multithreading also requires less processing overhead than
multiprogramming because concurrent threads are able to share common resources more effectively.
An example of a multithreaded application is a multithreaded Web server, which is able to efficiently
handle multiple browser requests—one request per each processing thread.

Creating Threads

Java, unlike many other programming languages, provides native support for multithreading. This
support is centered around the java.lang.Thread class, the java.lang.Runnable interface, and
methods of the java.lang.Object class. Support is also provided through synchronized methods
and statements.
The Thread class provides the capability to create objects of class Thread, each with their own
separate flow of control. The Thread class encapsulates the data and methods associated with
separate threads of execution and enables multithreading to be integrated within Java's object-oriented
framework. The minimal multithreading support required of the Thread class (or other classes that
support multithreading) is specified by the java.lang.Runnable interface. This interface defines an
important method—the run() method—which provides the entry point for a separate thread of
execution. The Object class supports multithreading through the wait(), notify(), and
notifyAll() methods. These methods allow threads to suspend their execution to wait for processing
resources and to be informed when to resume execution as the resources become available. Other
classes of the java.lang package, such as ThreadGroup, also support multithreading.
Java provides two approaches to creating threads. In the first approach, you create a subclass of class
Thread and override the run() method to provide an entry point into the thread's execution. When you
create an instance of your Thread subclass, you invoke its start() method to cause the thread to
execute as an independent sequence of instructions. The start() method is inherited from the
Thread class. It initializes the Thread object, using your operating system's multithreading capabilities,
and invokes the run() method. Listing 8.1 provides an example of creating threads in this manner.
When I run the program on my computer, it displays the following output:

thread2: Java

thread1: Java

thread2: is

thread1: is

thread2: hot,

thread1: hot,

thread1: aromatic,

thread2: aromatic,

- 124



thread2: and

thread1: and

thread2: invigorating.

Thread 2 is dead.

thread1: invigorating.

Thread 1 is dead.

Note
Java API Documentation This chapter makes heavy use of the Java API
methods defined for class Thread, Throwable, and Object. If you haven't
obtained and installed a copy of the Java API documentation, now is a good time
to do so. The API documentation is available at JavaSoft's Web site,
http://www.javasoft.com.

The above output shows how two threads execute in sequence, displaying information to the console
window. The program creates two threads of execution, thread1 and thread2, from the MyThreadclass. It then starts both

threads and executes a do statement that waits for the threads to die. The
threads display the Java is hot, aromatic, and invigorating. message word by word,
while waiting a short, random amount of time between each word. Because both threads share the
console window, the program's output identifies which threads wrote to the console at various times

during the program's execution.
Listing 8.1: Thread1.java—Subclassing the Thread Class
class Thread1 extends Thread {
static String message[] = {"Java","is","hot,","aromatic,","and",
"invigorating."};

public static void main(String args[]) {
Thread1 thread1 = new Thread1("thread1: ");
Thread1 thread2 = new Thread1("thread2: ");
thread1.start();
thread2.start();
boolean thread1IsAlive = true;
boolean thread2IsAlive = true;
do {

if(thread1IsAlive && !thread1.isAlive())
{
thread1IsAlive = false;
System.out.println("Thread 1 is dead.")
;


}

if(thread2IsAlive && !thread2.isAlive())
{
thread2IsAlive = false;
System.out.println("Thread 2 is dead.")
;


}

- 125



}while(thread1IsAlive || thread2IsAlive);

}

public Thread1(String id) {
super(id);

}

public void run() {
String name = getName();
for(int i=0;irandomWait();
System.out.println(name+message[i]);
}

}

void randomWait(){
try {
sleep((long)(3000*Math.random()));
}catch (InterruptedException x){
System.out.println("Interrupted!");
}

}

}
The approach to creating threads shown in Listing 8.1 is very simple and straightforward. However, it
has the drawback of requiring your Thread objects to be under the Thread class in the class
hierarchy. In some cases, such as applets, this requirement can be somewhat limiting.
Java's other approach to creating threads does not limit the location of your Thread objects within the
class hierarchy. In this approach, your class implements the java.lang.Runnable interface. The
Runnable interface consists of a single method, the run() method, which must be overridden by your
class. The run() method provides an entry point into your thread's execution. In order to run an object
of your class as an independent thread, you pass it as an argument to a constructor of class Thread.
Listing 8.2 shows how this is done.

Listing 8.2: Thread2.java—Implementing Runnable
class Thread2
{
public static void main(String args[])
{
Thread thread1 = new Thread(new MyClass("thread1: "))
;


- 126



Thread thread2 = new Thread(new MyClass("thread2: "))
;
thread1.start()
;
thread2.start()
;
boolean thread1IsAlive = true;
boolean thread2IsAlive = true;
do
{


if(thread1IsAlive && !thread1.isAlive())
{
thread1IsAlive = false;
System.out.println("Thread 1 is dead.")
;


}

if(thread2IsAlive && !thread2.isAlive())
{
thread2IsAlive = false;
System.out.println("Thread 2 is dead.")
;


}

}while(thread1IsAlive || thread2IsAlive);

}

}

class MyClass implements Runnable {
static String message[] = {"Java","is","hot,","aromatic,","and",
"invigorating."};
String name;
public MyClass(String id) {
name = id;
}
public void run() {
for(int i=0;irandomWait();
System.out.println(name+message[i]);
}
}
void randomWait(){

- 127



try {
Thread.currentThread().sleep((long)(3000*Math.random()));
}catch (InterruptedException x){
System.out.println("Interrupted!");
}

}

}
The Thread2 program is very similar to Thread1. It even displays the same ouput when I run it on my
computer. Thread2 differs from Thread1 only in the way that the threads are created.
Because these two examples are so similar, you might be wondering why you would pick one approach
to creating a class over another. The advantage of using the Runnable interface is that your class does
not need to extend the Thread class. This is a very helpful feature when you create multithreaded
applets. The only disadvantage to this approach is that you have to do a little more work to create and
execute your threads.

Thread States

In the discussion of Listings 8.1 and 8.2, I referred to threads as being dead when their processing had
completed. I wasn't trying to be morose. This is the standard Java term for referring to that state of a
thread's execution. After a thread reaches the dead state, it can't be restarted. The thread still exists as
an object (that is as a Thread or Runnable object), it just doesn't execute as a separate thread of
execution.

Threads have several other well-defined states in addition to the dead state. These states are

.
Ready—When a thread is first created, it doesn't begin executing immediately. You must
first invoke its start() method, and then the thread scheduler must allocate it CPU time. A
thread may also enter the ready state if, after previously executing, it stopped for a while and
then became ready to resume its execution.
.
Running—Threads are born to run. A thread is in the running state when it is actually
executing. It may leave this state for a number of reasons, which we'll cover in this section
and next section.
.
Waiting—A running thread may perform a number of actions that will cause it to wait. A
common example is when the thread performs some type of input or output operation. In
Listings 8.1 and 8.2, threads are waiting because they invoked their sleep() methods.
In general, a thread is either ready, running, waiting, or dead. Figure 8.2 shows the interaction between
these states.

A thread begins as a ready thread and then enters the running state when it is scheduled by the thread
scheduler. The thread may be preempted by other threads and returned to the ready state, or it may
wait on a resource, object lock, or simply go to sleep. When this happens, the thread enters the waiting
state. To run again, the thread must enter the ready state. Eventually, the thread will cease its execution
and enter the dead state.


Figure 8.2: Interaction between thread states.

- 128



Scheduling

After examining the thread state diagram shown in Figure 8.2, you may wonder exactly what causes a
thread to move from one state to another. In this section, we'll look at how threads move between the
ready and running state. In later sections, we'll look at movement in and out of the waiting state.

A thread moves back and forth between the ready and running state because multithreading requires
multiple threads to share execution time with each other, based on the availability of the system's CPU
(or CPUs). The approach used to determine which threads should execute at a given time is referred to
as scheduling. Scheduling is performed by the Java runtime system. It schedules threads based on their
priority. Higher priority threads are run before lower priority threads. Threads of equal priority have an
equal chance of running. While the above rules guide thread scheduling, the actual details of thread
scheduling are platform-dependent.

Most operating systems support one of two common approaches to thread scheduling:

.
Preemptive scheduling—The highest priority thread continues to execute unless it dies,
waits, or is preempted by a higher priority thread coming into existence. The latter can occur
as the result of a thread lowering its priority or creating a higher priority thread.
Note
Thread Priority A thread's priority is an integer value between MIN_PRIORITY
and MAX_PRIORITY. These constants are defined in the Thread class. A
thread's priority is set when it is created. It is set to the same priority as the thread
that created it. The default priority of a thread is NORM_PRIORITY. The priority of
a thread can be changed using the setPriority() method.

.
Time slicing—A thread executes for a specific slice of time and then enters the ready state.
At this point, the thread scheduler determines whether it should return the thread to the
ready state or schedule a different thread.
Both approaches have their advantages and disadvantages. Preemptive scheduling is more predictable.
However, its disadvantage is that a higher priority thread could execute forever, preventing lower priority
threads from executing. The time slicing approach is less predictable but better able to handle selfish
threads. Windows and Macintosh implementations of Java follow a time slicing approach. Solaris and
other Unix implementations follow a preemptive scheduling approach.

Note
Yielding The simplest way for a running thread to enter the ready state is for it to

invoke its yield() method. The yield() method is a static method of the

Thread class. When a thread invokes its yield() method, it simply returns to

the ready state. It then returns to the running state at the discretion of the thread

scheduler.

Sleeping and Waking

As shown in Listings 8.1 and 8.2, a thread can enter the waiting state by invoking its sleep() method.
The sleep() method, like the yield() method, is a static method of the Thread class. It takes a
millisecond time value and an optional nanosecond time value as arguments. When a thread invokes its
sleep() method, it enters the waiting state. It returns to the ready state after the specified time has
expired.
It is possible for another thread to awaken a sleeping thread by invoking the sleeping thread's
interrupt() method. The sleeping thread then enters the ready state. When it reenters the running
state, execution continues with the thread's InterruptedException handler.

Stopping, Suspending, and Resuming

Prior to JDK 1.2, it was possible for one thread to stop or suspend the execution of another thread (or
itself). The stop(), suspend(), and resume() methods have been identified as problematic and are
deprecated in JDK 1.2.

Blocking on I/O

A thread may also enter the waiting state as the result of performing an input/output operation. I/O
operations, such as disk writes or socket reads, are very slow when compared to the execution speed of
a CPU. While one thread waits for an I/O operation to complete, it is possible for other threads to
perform quite a bit of processing. This is illustrated in Figure 8.3. When a thread performs an I/O
operation, it enters the waiting state. It remains in the waiting state until the I/O operation is completed.
The thread is said to be blocked on I/O or blocking on I/O. When the I/O operation is completed, the
thread transitions to the ready state.

- 129



Figure 8.3: Blocking on I/O.

Synchronization

There are many situations in which multiple threads must share access to common objects. For
example, Listings 8.1 and 8.2 illustrate the effects of multithreading by having multiple executing threads
write to the Java console, a common shared object. These examples do not require any coordination or
synchronization in the way the threads access the console window: Whichever thread is currently
executing is able to write to the console window.
There are times when you might want to coordinate access to shared resources. For example, in a
database system, you might not want one thread to be updating a database record while another thread
is trying to read it. Java enables you to coordinate the actions of multiple threads using synchronized
methods and synchronized statements. I'll cover synchronized methods first,and then
synchronized statements.
Synchronized methods are used to coordinate access to objects that are shared among multiple
threads. These methods are declared with the synchronized keyword. Only one synchronizedmethod can be invoked for an object

at a given point in time. This keeps synchronized methods in
multiple threads from conflicting with each other.
All classes and objects are associated with a unique lock. The lock is used to control the way in which
synchronized methods are allowed to access the class or object. When a synchronized method is
invoked for a given object, it tries to acquire the lock for that object. If it succeeds, no other
synchronized method may be invoked for that object until the lock is released. A lock is automatically
released when the method completes its execution and returns. A lock may also be released when a
synchronized method executes certain methods, such as wait(). We'll cover the wait() method in
a later section.

Note
Class Locks Although we speak of a lock on a class, technically a class lock is a
lock that is acquired on the class's Class object.

Note
Locks and States When a thread is waiting for a lock on an object, the thread
leaves the running state and enters the waiting state. When the thread acquires
the lock on the object, it moves from the waiting state to the ready state.

The following example shows how synchronized methods and object locks are used to coordinate
access to a common object by multiple threads. This example adapts the Thread1 program for use
with synchronized methods, as shown in Listing 8.3.

SynchronizedListing 8.3: Using Methods

class Thread3 extends Thread {

static String message[] = {"Java","is","hot,","aromatic,","and",

"invigorating."};

public static void main(String args[]) {

Thread3 thread1 = new Thread3("thread1: ");

- 130



Thread3 thread2 = new Thread3("thread2: ")
;
thread1.start()
;
thread2.start()
;
boolean thread1IsAlive = true;
boolean thread2IsAlive = true;
do
{


if(thread1IsAlive && !thread1.isAlive())
{
thread1IsAlive = false;
System.out.println("Thread 1 is dead.")
;


}

if(thread2IsAlive && !thread2.isAlive())
{
thread2IsAlive = false;
System.out.println("Thread 2 is dead.")
;


}

}while(thread1IsAlive || thread2IsAlive);
}
public Thread3(String id) {

super(id);
}
public void run() {

SynchronizedOutput.displayList(getName(),message);
}
void randomWait(){

try
{
sleep((long)(3000*Math.random()))
;
}catch (InterruptedException x)
{
System.out.println("Interrupted!")
;
}


}

}

class SynchronizedOutput {

- 131



public static synchronized void
displayList(String name,String list[]) {
for(int i=0;i
Thread3 t = (Thread3) Thread.currentThread();
t.randomWait();
System.out.println(name+list[i]);

}
}

}
Here are the results of an example run on my system:
thread1: Java
thread1: is
thread1: hot,
thread1: aromatic,
thread1: and
thread1: invigorating.
Thread 1 is dead.
thread2: Java
thread2: is
thread2: hot,
thread2: aromatic,
thread2: and
thread2: invigorating.
Thread 2 is dead.


Now edit Thread3.java and delete the synchronized keyword in the declaration of the


displayList() method of class SynchronizedOutput. It should look like this when you are


finished:


class SynchronizedOutput
{
public static void displayList(String name,String list[])
{


Save Thread3.java, recompile it, and rerun it with the change in place. You may now get output
similar to this:
thread2: Java
thread1: Java
thread1: is
thread2: is
thread2: hot,
thread1: hot,
thread2: aromatic,
thread1: aromatic,


- 132



thread2: and

thread2: invigorating.

Thread 2 is dead.

thread1: and

thread1: invigorating.

Thread 1 is dead.

The difference in the program's output should give you a feel for the effects of synchronization upon
multithreaded program execution.
What difference does the fact that displayList() is synchronized have on the program's
execution? When displayList() is not synchronized, it may be invoked by one thread (for example
thread1), display some output, and wait while thread2 executes. When thread2 executes, it too
invokes displayList() to display some output. Two separate invocations of displayList(), one
for thread1 and the other for thread2, execute concurrently. This explains the mixed output display.
When the synchronized keyword is used, thread1 invokes displayList() and acquires a lock
for the SynchronizedOutput class (because displayList() is a static method). After this,
displayList() proceeds with the output display for thread1. Because thread1 acquired a lock for
the SynchronizedOutput class, thread2 must wait until the lock is released before it is able to
invoke displayList() to display its output. This explains why one task's output is completed before
the other's.

Note
Static Synchronized Methods Methods that are declared as both static and
synchronized must acquire the lock on their class (their class's Class object)
before they are allowed to execute. That's because static methods operate on
their class as a whole rather than on instances of their class.

The synchronized Statement

The synchronized statement is similar to a synchronized method in that it is used to acquire a lock
on an object before performing an action. The synchronized statement differs from a synchronizedmethod in that it can be used

with the lock of any object—the synchronized method can only be used
with its object's (or class's) lock. It also differs in that it applies to a statement block, rather than an
entire method. The syntax of the synchronized statement is as follows:

synchronized (object) {

statement(s)

}
The statements enclosed by the braces are only executed when the current thread acquires the lock for
the object or class enclosed by parentheses. Listing 8.4 shows how the synchronized statement may
be used in this chapter's extended example.

SynchronizedListing 8.4: Using the Statement

class Thread4 extends Thread {
static String message[] = {"Java","is","hot,","aromatic,","and",
"invigorating."};

public static void main(String args[]) {
Thread4 thread1 = new Thread4("thread1: ");
Thread4 thread2 = new Thread4("thread2: ");
thread1.start();
thread2.start();
boolean thread1IsAlive = true;

- 133



boolean thread2IsAlive = true;

do {

if(thread1IsAlive && !thread1.isAlive()){
thread1IsAlive = false;
System.out.println("Thread 1 is dead.");

}

if(thread2IsAlive && !thread2.isAlive()){
thread2IsAlive = false;
System.out.println("Thread 2 is dead.");

}

}while(thread1IsAlive || thread2IsAlive);
}
public Thread4(String id) {

super(id);
}
public void run() {

synchronized(System.out) {

for(int i=0;irandomWait();
System.out.println(getName()+message[i]);

}

}
}
void randomWait(){

try {
sleep((long)(3000*Math.random()));
}catch (InterruptedException x){
System.out.println("Interrupted!");
}
}
}

- 134



Waiting and Notifying

The wait(), notify(), and notifyAll() methods of the Object class are used to provide an
efficient way for threads to wait for resources. They also provide additional synchronization support.
These methods are used to implement a synchronization scheme where threads that have acquired an
object's (or class's) lock, wait until they are notified that access to the object is permitted. The wait(),
notify() and notifyAll() methods provide you with finer control over shared resources. They
enable you to place threads in a waiting pool until resources become available to satisfy the threads'
requests. A separate resource monitor or controller process can be used to notify waiting threads that
they are able to execute.
When a thread has acquired an object's lock, either through a synchronized method or a
synchronized statement, the thread invokes the object's wait() method. This causes the thread to
lose the object's lock and enter the waiting state. It is possible for several threads to be waiting as the
result of invoking an object's wait() method. These threads are referred to as the waiting pool.
A thread typically invokes an object's wait() method by invoking a synchronized method of the
object. The call to wait() is embedded in the synchronized method. For example, a service-
providing object would provide a method of the following form:

public synchronized returnType requestService(parameters) {

while(resourceNotAvailable) {

wait();

}

// Perform service

}
When another thread acquires the object's lock and invokes the object's notify() method, one of the
waiting threads is returned to the ready state. (The notifyAll() method is used to return all of the
threads in the waiting pool to the ready state.) When a waiting thread reenters the running state, it
reacquires the lock on the object and can then continue its processing.
Listing 8.5 shows how the wait() and notify() methods are used to control access to a shared
resource. A Resource object is shared among a Controller object and several User objects. The
Resource object provides access to the console display. The User objects execute as separate
threads and use the Resource object to display their output. The Controller object also executes as
a separate thread and controls access to the Resource object, making it available for output every 10
seconds.
The displayOutput() method of the Resource object displays a message on the console when
okToSend is true. If a User thread invokes displayOutput() when okToSend is false, it enters the
waiting pool. The allowOutput() method of Resource is periodically invoked by the Controller to
allow a User thread to exit the waiting pool and display output via the Resource object.
Note that the wait() method, like the sleep() method of the Thread class, throws the
InterruptedException. This exception is thrown when a thread's interrupt() method is invoked
while it is in the waiting state.

Notify()Wait()Listing 8.5: Using and to Control Access to a Shared Resource

class Thread5 {

public static void main(String args[]) {
Resource resource = new Resource();
Thread controller = new Thread(new Controller(resource));
Thread[] user = new Thread[3];
for(int i=0;i
user[i] = new Thread(new User(i,resource));

- 135



controller.start()
;
for(int i=0;i

user[i].start()
;
boolean alive;
out: do
{


alive = false;
for(int i=0;ialive |= user[i].isAlive()
;


Thread.currentThread().yield()
;
} while(alive)
;
controller.interrupt()
;


}
}
class Resource {

boolean okToSend = false;
public synchronized void displayOutput(int id,String[] message) {
try{
while(!okToSend) {

wait()
;
}
okToSend = false;
for(int i=0;i{


Thread.currentThread().sleep((long)1000)
;
System.out.println(id+": "+message[i])
;


}
}catch(InterruptedException ex)
{
}


}

public synchronized void allowOutput() {
okToSend = true;
notify();

- 136



}

}

class Controller implements Runnable {
Resource resource;
public Controller(Resource resource) {
this.resource = resource;
}
public void run() {
try{
while(true) {
Thread.currentThread().sleep((long)10000);
resource.allowOutput();
}
}catch(InterruptedException ex){
}
}

}

class User implements Runnable {
static String message[] = {"Java","is","hot,","aromatic,","and",
"invigorating."};
int id;
Resource resource;
public User(int id,Resource resource) {
this.id = id;
this.resource = resource;
}
public void run() {
resource.displayOutput(id,message);
}

}
- 137



Note
Class
Synchroniz
ation
A
thread may
synchronize
on a class
as well as
an object of
a class.


Chapter Summary

This chapter described how multithreading works, and how multithreading is supported by Java. It also
covered the thread-related topics of scheduling and synchronization. It explained how thread prority is
used to determine which thread is executed. It also explained how object locks are used to control
access to shared resources. You should now be prepared to test your knowledge of these topics. The
following review questions and exam questions will let you know how well you understand this material
and will give you an idea of how you'll do in thread-related exam questions. They'll also indicate which
material you need to study further.

Key Terms

.
Multithreading
.
Thread state
.
Scheduling
.
Shared resource
.
Synchronization
.
Object lock
Review Questions

1.
How does multithreading take place on a computer with a single CPU?
2.
What are the two basic ways in which classes that can be run as threads may be
defined?
3.
What method must be implemented by all threads?
4.
What method is invoked to cause an object to begin executing as a separate thread?
5.
What invokes a thread's run() method?
6.
What are the high-level thread states?
7.
When a thread is created and started, what is its initial state?
8.
What state is a thread in when it is executing?
9.
When a thread blocks on I/O, what state does it enter?
10. What state does a thread enter when it terminates its processing?
11. How can a dead thread be restarted?
12. What are three ways in which a thread can enter the waiting state?
13. What is a task's priority and how is it used in scheduling?
14. What is the difference between preemptive scheduling and time slicing?
15. What is the difference between yielding and sleeping?
16. What happens when you invoke a thread's interrupt method while it is sleeping or
waiting?
17. What's new with the stop(), suspend() and resume() methods in JDK 1.2?
18. Why do threads block on I/O?
- 138



19. What is synchronization and why is it important?
20. What is an object's lock and which object's have locks?
21. Can a lock be acquired on a class?
22. What are synchronized methods and synchronized statements?
23. What happens when a thread cannot acquire a lock on an object?
24. What is the purpose of the wait(), notify(), and notifyAll() methods?
Exam Questions

1.
Which of the following are true?
A.
Multithreading is unique to Java.
B.
Multithreading requires more than one CPU.
C.
Multithreading requires that a computer have a single CPU.
D.
Multithreading is supported by Java.
2.
How can a class that is run as a thread be defined?
A.
By subclassing the Thread class.
B.
By implementing the Throwable interface.
C.
By implementing the Multithread interface.
D.
By implementing the Runnable interface.
3.
The Runnable interface declares which methods?
A.
start()
B.
run()
C.
stop()
D.
yield()
4.
Given an object t that implements Runnable, which method of the Thread class
should you invoke to cause t to be executed as a separate thread?
A.
start()
B.
init()
C.
run()
D.
main()
5.
Which of the following are thread states?
A. ready
B. running
C. open
D. waiting
E. dead
F.
unwound
6.
Which of the following thread state transitions are valid?
A.
From ready to running.
B.
From running to ready.
C.
From running to waiting.
D.
From waiting to running.
E.
From waiting to ready.
F.
From ready to waiting.
7.
When a thread blocks on I/O, which of the following are true?
A.
The thread enters the ready state.
B.
The thread enters the dead state.
C.
No other thread may perform I/O.
D.
The thread enters the waiting state.
8.
Which of the following are true about a dead thread?
A.
The thread's object is discarded.
B.
The thread must wait until all other threads execute before it is restarted.
- 139



C.
The thread cannot be restarted.
D.
The thread is synchronized.
9.
Which of the following are true?
A.
Java only supports preemptive scheduling.
B.
Java only supports time slicing.
C.
The JVM has been implemented on operating systems that use time
slicing.
D.
The JVM has been implemented on operating systems that use preemptive
scheduling.
10. Which of the following are true?
A. The sleep() method puts a thread in the ready state.
B. The yield() method puts a thread in the waiting state.
C. The suspend() method is the preferred method for stopping a thread's
execution.
D. A thread's interrupt() method results in the throwing of the
InterruptedException.
11. Which of the following are true?
A.
Only threads have locks.
B.
Classes have locks.
C.
Primitive types have locks.
D. Only Runnable objects have locks.
12. Which of the following are true?
A. The Thread class inherits the wait() and notify() methods.
B. The Object class declares the wait() and notify() methods.
C. Only the Synchronized class supports the wait() and notify()
methods.
D. The wait() and notify() methods have been deprecated in JDK 1.2.
13. Which of the following lines of output are displayed by the following program?
14. class Question {
15. public static void main(String args[]) {
16. MyThread t = new MyThread();
17. t.displayOutput("t has been created.");
18. t.start();
19. }
20. }
21. class MyThread extends Thread {
22. public void displayOutput(String s) {
23. System.out.println(s);
24. }
25. public void run() {
26. displayOutput("t is running.");
27.
}
}
A.
t has been created.
B.
t is running.
C.
The program will display different output depending on whether the
underlying operating system supports preemptive scheduling or time
slicing.
D.
None of the above. The program does not compile.
28. Which of the following are true about the following program?
- 140



29. class Question {
30. public static void main(String args[]) {
31. MyThread t1 = new MyThread("t1");
32. MyThread t2 = new MyThread("t2");
33. t1.start();
34. t2.start();
35. }
36. }
37. class MyThread extends Thread {
38. public void displayOutput(String s) {
39. System.out.println(s);
40. }
41. public void run() {
42. for(int i=0;i<10;++i) {
43. try {
44. sleep((long)(3000*Math.random()));
45. }catch(Exception ex) {
46. }
47. displayOutput(getName());
48. }
49. }
50. public MyThread(String s) {
51. super(s);
52.
}
}
A. The program always displays t1 10 times, followed by t2 10 times.
B. The program always displays t1 followed by t2.
C. The program displays no output.
D. The output sequence will vary from computer to computer.
53. Which of the following are true about the following program?
54. class Question {
55. public static void main(String args[]) {
56. MyThread t1 = new MyThread("t1");
57. MyThread t2 = new MyThread("t2");
58. t1.start();
59. t2.start();
60. }
61. }
62. class MyThread extends Thread {
63. public static Resource resource = new Resource();
64. public void run() {
65. for(int i=0;i<10;++i) {
66. resource.displayOutput(getName());
67. }
68. }
- 141



69. public MyThread(String s) {
70. super(s);
71. }
72. }
73. class Resource {
74. public Thread controller;
75. boolean okToSend = false;
76. public synchronized void displayOutput(String s) {
77. try{
78. while(!okToSend) {
79. wait();
80. }
81. okToSend = false;
82. System.out.println(s);
83. }catch(InterruptedException ex){
84. }
85. }
86. public synchronized void allowOutput() {
87. okToSend = true;
88. notifyAll();
89. }
90. public Resource() {
91. Thread controller = new Thread(new Controller(this));
92. controller.start();
93. }
94. }
95. class Controller implements Runnable {
96. Resource resource;
97. public Controller(Resource resource) {
98. this.resource = resource;
99. }
100. public void run() {
101. try{
102. for(int i=0;i<100;++i) {
103. Thread.currentThread().sleep(2000);
104. resource.allowOutput();
105. }
106. }catch(InterruptedException ex){
107. }
108. }
}
A. The program always displays t1 10 times, followed by t2 10 times.
B. The program always displays t1 followed by t2.
C. The program displays no output.
D. The output sequence will vary from computer to computer.
- 142



Answers to Review Questions

1.
The operating system's task scheduler allocates execution time to multiple tasks. By
quickly switching between executing tasks, it creates the impression that tasks execute
sequentially.
2.
A thread class may be declared as a subclass of Thread, or it may implement the
Runnable interface.
3.
All tasks must implement the run() method, whether they are a subclass of Thread or
implement the Runnable interface.
4.
The start() method of the Thread class is invoked to cause an object to begin
executing as a separate thread.
5.
After a thread is started, via its start() method or that of the Thread class, the JVM
invokes the thread's run() method when the thread is initially executed.
6.
The high-level thread states are ready, running, waiting, and dead.
7.
A thread is in the ready state after it has been created and started.
8.
An executing thread is in the running state.
9.
A thread enters the waiting state when it blocks on I/O.
10. When a thread terminates its processing, it enters the dead state.
11. A dead thread cannot be restarted.
12. A thread can enter the waiting state by invoking its sleep() method, by blocking on
I/O, by unsuccessfully attempting to acquire an object's lock, or by invoking an object's
wait() method. It can also enter the waiting state by invoking its (deprecated)
suspend() method.
13. A task's priority is an integer value that identifies the relative order in which it should be
executed with respect to other tasks. The scheduler attempts to schedule higher priority
tasks before lower priority tasks.
14. Under preemptive scheduling, the highest priority task executes until it enters the waiting
or dead states or a higher priority task comes into existence. Under time slicing, a task
executes for a predefined slice of time and then reenters the pool of ready tasks. The
scheduler then determines which task should execute next, based on priority and other
factors.
15. When a task invokes its yield() method, it returns to the ready state. When a task
invokes its sleep() method, it returns to the waiting state.
16. When a task's interrupt() method is executed, the task enters the ready state. The
next time the task enters the running state, an InterruptedException is thrown.
17. The stop(), suspend() and resume() methods have been deprecated in JDK 1.2.
18. Threads block on I/O (that is enters the waiting state) so that other threads may execute
while the I/O operation is performed.
19. With respect to multithreading, synchronization is the capability to control the access of
multiple threads to shared resources. Without synchronization, it is possible for one
thread to modify a shared object while another thread is in the process of using or
updating that object's value. This often leads to significant errors.
20. An object's lock is a mechanism that is used by multiple threads to obtain synchronized
access to the object. A thread may execute a synchronized method of an object only
after it has acquired the object's lock. All objects and classes have locks. A class's lock
is acquired on the class's Class object.
21. Yes, a lock can be acquired on a class. This lock is acquired on the class's Class
object.
22. Synchronized methods are methods that are used to control access to an object. A
thread only executes a synchronized method after it has acquired the lock for the
method's object or class. Synchronized statements are similar to synchronizedmethods. A synchronized statement can only be

executed after a thread has acquired
the lock for the object or class referenced in the synchronized statement.
23. If a thread attempts to execute a synchronized method or synchronized statement
and is unable to acquire an object's lock, it enters the waiting state until the lock
becomes available.
24. The wait(),notify(), and notifyAll() methods are used to provide an efficient
way for threads to wait for a shared resource. When a thread executes an object's
- 143



wait() method, it enters the waiting state. It only enters the ready state after another
thread invokes the object's notify() or notifyAll() methods.

Answers to Exam Questions

1.
D. Multithreading is not unique to Java and can be implemented on systems with one or
more CPUs.
2.
A and D. Subclassing Thread and implementing Runnable are the two approached to
creating a thread class.
3.
B. The run() method is the only method declared by Runnable.
4.
A. The start() method is used to execute an object as a separate thread.
5.
A, B, D, and E. Thread states are ready, running, waiting, and dead.
6.
A, B, C, E. Threads do not transition from waiting to running or ready to waiting.
7.
D. Threads enter the waiting state when they block on I/O.
8.
C. Dead threads may not be restarted.
9.
C and D. Solaris supports preemptive scheduling, and Windows supports time slicing.
10. D. The interrupt() method results in the throwing of the InterruptedExceptionwhen an interrupted thread enters the

running state.
11. B. Classes have locks, but primitive types do not.
12. A and B. A follows from B.
13. A and B. Both A and B are displayed.
14. D. The output may vary because it is not synchronized.
15. D. The output is only synchronized one line at a time.

No comments: