Java Programming Chapter 11
Java Programming Chapter 11
IN JAVA
Dr. Manish Kumar
Assistant Professor
Department of Master of Computer Applications
M S Ramaiah Institute of Technology
Bangalore-54
The Java run-time system depends on threads for many things, and all the class libraries are
designed with multithreading in mind.
The benefit of Java’s multithreading is that the main loop/polling mechanism is eliminated.
One thread can pause without stopping other parts of your program.
It is important to understand that Java’s multithreading features work in both types of systems.
In a single core system, concurrently executing threads share the CPU, with each thread
receiving a slice of CPU time.
Therefore, in a single-core system, two or more threads do not actually run at the same time,
but idle CPU time is utilized. However, in multi-core systems, it is possible for two or more
threads to actually execute simultaneously.
In many cases, this can further improve program efficiency and increase the speed of certain
operations.
Java assigns to each thread a priority that determines how that thread should be treated with
respect to the others.
Thread priorities are integers that specify the relative priority of one thread to another.
As an absolute value, a priority is meaningless; a higher-priority thread doesn’t run any faster
than a lower-priority thread if it is the only thread running.
Instead, a thread’s priority is used to decide when to switch from one running thread to the
next. This is called a context switch.
In cases where two threads with the same priority are competing for CPU cycles, the situation
is a bit complicated.
For operating systems such as Windows, threads of equal priority are time-sliced
automatically in round-robin fashion.
For other types of operating systems, threads of equal priority must voluntarily yield control
to their peers. If they don’t, the other threads will not run.
Java’s multithreading system is built upon the Thread class, its methods, and
its companion interface, Runnable.
Thread encapsulates a thread of execution. Since you can’t directly refer to
the ethereal state of a running thread, you will deal with it through its proxy,
the Thread instance that spawned it.
To create a new thread, your program will either extend Thread or
implement the Runnable interface.
The Thread class defines several methods that help manage threads.
When a Java program starts up, one thread begins running immediately. This is
usually called the main thread of your program, because it is the one that is executed
when your program begins.
The main thread is important for two reasons:
It is the thread from which other “child” threads will be spawned.
Often, it must be the last thread to finish execution because it performs various shutdown
actions.
Although the main thread is created automatically when your program is started, it
can be controlled through a Thread object. To do so, you must obtain a reference to
it by calling the method currentThread( ), which is a public static member of Thread.
Its general form is shown here:
static Thread currentThread( )
try {
for(int n = 5; n > 0; n--) {
System.out.println(n);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
System.out.println("Main thread interrupted");
}
}
}
The easiest way to create a thread is to create a class that implements the Runnable
interface. Runnable abstracts a unit of executable code.
You can construct a thread on any object that implements Runnable.
To implement Runnable, a class need only implement a single 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 can.
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.
NewThread() { try {
// Create a new, second thread for(int i = 5; i > 0; i--) {
t = new Thread(this, "Demo Thread"); System.out.println("Main Thread: " + i);
System.out.println("Child thread: " + t); Thread.sleep(1000);
t.start(); // Start the thread }
} } catch (InterruptedException e) {
System.out.println("Main thread interrupted.");
// This is the entry point for the second thread. }
public void run() { System.out.println("Main thread exiting.");
try { }
for(int i = 5; i > 0; i--) { }
System.out.println("Child Thread: " + i);
Thread.sleep(500);
}
} catch (InterruptedException e) {
System.out.println("Child interrupted.");
}
System.out.println("Exiting child thread.");
}
}
The second way to create a thread is to create a new class that extends Thread, and then to create an
instance of that class. The extending class must override the run( ) method, which is the entry point for
the new thread. It must also call start( ) to begin execution of the new thread. Here is the preceding
program rewritten to extend Thread:
// Create a second thread by extending Thread class ExtendThread {
class NewThread extends Thread { public static void main(String args[]) {
new NewThread(); // create a new thread
NewThread() {
// Create a new, second thread try {
super("Demo Thread"); for(int i = 5; i > 0; i--) {
System.out.println("Child thread: " + this); System.out.println("Main Thread: " + i);
start(); // Start the thread Thread.sleep(1000);
} }
} catch (InterruptedException e) {
// This is the entry point for the second thread. System.out.println("Main thread interrupted.");
public void run() { }
try { System.out.println("Main thread exiting.");
for(int i = 5; i > 0; i--) { }
System.out.println("Child Thread: " + i); }
Thread.sleep(500); This program generates the same output as the preceding version. As
} you can see, the child thread is created by instantiating an object of
} catch (InterruptedException e) {
System.out.println("Child interrupted.");
NewThread, which is derived from Thread.
}
System.out.println("Exiting child thread.");
}
}
Notice the call to super( ) inside NewThread. This invokes the following form of the
Thread constructor:
So far, you have been using only two threads: the main thread and one child
thread. However, your program can spawn as many threads as it needs. For
example, the following program creates three child threads:
New thread: Thread[One,5,main] As you can see, once started, all three child threads share
New thread: Thread[Two,5,main] the CPU. Notice the call to sleep(10000) in main( ). This
New thread: Thread[Three,5,main] causes the main thread to sleep for ten seconds and ensures
One: 5
that it will finish last.
Two: 5
Three: 5
One: 4
Two: 4
Three: 4
One: 3
Three: 3
Two: 3
One: 2
Three: 2
Two: 2
One: 1
Three: 1
Two: 1
One exiting.
Two exiting.
Three exiting.
Main thread exiting.
As mentioned, often you will want the main thread to finish last. In the preceding
examples, this is accomplished by calling sleep( ) within main( ), with a long enough
delay to ensure that all child threads terminate prior to the main thread. However, this
is hardly a satisfactory solution, and it also raises a larger question: How can one
thread know when another thread has ended? Fortunately, Thread provides a means
by which you can answer this question.
Two ways exist to determine whether a thread has finished. First, you can call isAlive( )
on the thread. This method is defined by Thread, and its general form is shown here:
final boolean isAlive( )
The isAlive( ) method returns true if the thread upon which it is called is still running.
It returns false otherwise.
While isAlive( ) is occasionally useful, the method that you will more commonly use to
wait for a thread to finish is called join( ), shown here:
final void join( ) throws InterruptedException
To set a thread’s priority, use the setPriority( ) method, which is a member of Thread.
This is its general form:
final void setPriority(int level)
Here, level specifies the new priority setting for the calling thread. The value of level
must be within the range MIN_PRIORITY and MAX_PRIORITY.
Currently, these values are 1 and 10, respectively. To return a thread to default priority,
specify NORM_PRIORITY, which is currently 5.
These priorities are defined as static final variables within Thread.
You can obtain the current priority setting by calling the getPriority( ) method of
Thread, shown here:
final int getPriority( )
When two or more threads need access to a shared resource, they need some way to ensure
that the resource will be used by only one thread at a time.
The process by which this is achieved is called synchronization.
Java provides unique, language-level support for it.
Key to synchronization is the concept of the monitor.
A monitor is an object that is used as a mutually exclusive lock. Only one thread can own a
monitor at a given time.
When a thread acquires a lock, it is said to have entered the monitor. All other threads
attempting to enter the locked monitor will be suspended until the first thread exits the monitor.
These other threads are said to be waiting for the monitor.
A thread that owns a monitor can reenter the same monitor if it so desires.
You can synchronize your code in either of two ways. Both involve the use of the synchronized
keyword.
To understand the need for synchronization, let’s begin with a simple example
that does not use it—but should.
The following program has three simple classes. The first one, Callme, has a single
method named call( ).
The call( ) method takes a String parameter called msg. This method tries to print
the msg string inside of square brackets. The interesting thing to notice is that
after call( ) prints the opening bracket and the msg string, it calls
Thread.sleep(1000), which pauses the current thread for one second.
• While creating synchronized methods within classes that you create is an easy
and effective means of achieving synchronization, it will not work in all cases.
• To understand why, consider the following. Imagine that you want to synchronize
access to objects of a class that was not designed for multithreaded access. That
is, the class does not use synchronized methods.
• Further, this class was not created by you, but by a third party, and you do not
have access to the source code.
• Thus, you can’t add synchronized to the appropriate methods within the class.
How can access to an object of this class be synchronized?
• Fortunately, the solution to this problem is quite easy: You simply put calls to the
methods defined by this class inside a synchronized block.
Dr. Manish Kumar, MSRIT, Bangalore-54
This is the general form of the synchronized statement:
synchronized(objRef) {
// statements to be synchronized
}
while(true) {
q.put(i++);
}
}
}
Although the put( ) and get( ) methods on Q are synchronized, nothing stops the producer from overrunning the consumer, nor
will anything stop the consumer from consuming the same queue value twice. Thus, you get the erroneous output shown here
(the exact output will vary with processor speed and task load):
Put: 1
Got: 1
Got: 1
Got: 1
Got: 1
Got: 1
Put: 2
Put: 3
Put: 4
Put: 5
Put: 6
Put: 7
Got: 7
Producer(Q q) { Consumer(Q q) {
this.q = q; this.q = q;
new Thread(this, "Producer").start(); new Thread(this, "Consumer").start();
} }
Put: 1
Got: 1
Put: 2
Got: 2
Put: 3
Got: 3
Put: 4
Got: 4
Put: 5
Got: 5
try {
System.out.println(name + " entered A.foo");
Thread.sleep(1000);
} catch(Exception e) {
try { System.out.println("B Interrupted");
Thread.sleep(1000); }
} catch(Exception e) {
System.out.println("A Interrupted"); System.out.println(name + " trying to call A.last()");
} a.last();
}
System.out.println(name + " trying to call B.last()");
b.last(); synchronized void last() {
System.out.println("Inside A.last");
}
}
}
synchronized void last() {
System.out.println("Inside A.last");
}
}
The mechanisms to suspend, stop, and resume threads differ between early versions
of Java, such as Java 1.0, and modern versions, beginning with Java 2.
Prior to Java 2, a program used suspend( ), resume( ), and stop( ), which are methods
defined by Thread, to pause, restart, and stop the execution of a thread. Although
these methods seem to be a perfectly reasonable and convenient approach to
managing the execution of threads, they must not be used for new Java programs.
Here’s why. The suspend( ) method of the Thread class was deprecated by Java 2
several years ago. This was done because suspend( ) can sometimes cause serious
system failures.
Assume that a thread has obtained locks on critical data structures. If that thread is
suspended at that point, those locks are not relinquished.
Other threads that may be waiting for those resources can be deadlocked.
Thread.State getState( )
It returns a value of type Thread.State that indicates the state of the thread at the
time at which the call was made. State is an enumeration defined by Thread.
Thread.State ts = thrd.getState();
if(ts == Thread.State.RUNNABLE) // ...
• It is important to understand that a thread’s state may change after the call to
getState( ).
• Thus, depending on the circumstances, the state obtained by calling getState( )
may not reflect the actual state of the thread only a moment later.
• For this (and other) reasons, getState( ) is not intended to provide a means of
synchronizing threads.
• It’s primarily used for debugging or for profiling a thread’s run-time
characteristics.