100% found this document useful (1 vote)
46 views

Multithreading Concept: by The End of This Chapter, You Will Be Able To

This document discusses multithreading concepts in Java. It defines threads as units of execution that allow programs to split into multiple concurrent tasks. The key points covered include: how threads are created using the Thread class and Runnable interface, the different states threads can be in, how to set thread priorities, the difference between daemon and non-daemon threads, and how to communicate between threads. Examples are provided throughout to illustrate multithreading concepts.

Uploaded by

Black Panda
Copyright
© © All Rights Reserved
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
100% found this document useful (1 vote)
46 views

Multithreading Concept: by The End of This Chapter, You Will Be Able To

This document discusses multithreading concepts in Java. It defines threads as units of execution that allow programs to split into multiple concurrent tasks. The key points covered include: how threads are created using the Thread class and Runnable interface, the different states threads can be in, how to set thread priorities, the difference between daemon and non-daemon threads, and how to communicate between threads. Examples are provided throughout to illustrate multithreading concepts.

Uploaded by

Black Panda
Copyright
© © All Rights Reserved
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 78

Chapter-2

Multithreading Concept
By the end of this chapter, you will be able to:
• Understand multithreading fundamentals

• Know the Thread class and the Runnable interface

• Create a thread

• Create multiple threads

• Determine when a thread ends

• Use thread priorities

• Understand thread synchronization

• Use synchronized methods

• Use synchronized blocks

• Communicate between threads


1
• Suspend, resume, and stop threads
Processes and Threads
A multitasking operating system creates several processes and runs them on multiple
CPUs or the cores of a single CPU.

In the rare situation that you have a single-core CPU in your box, the OS will switch the
CPU between these processes, giving you the illusion of concurrently running processes.

Processes are usually large and can be further split into smaller units of execution.
For example,
• A word processing application might perform a background spell check while the
user edits the document.

• A spreadsheet application may allow the user to interact with it while it performs
calculations in the background.
• To implement these kinds of features, the developer splits the process into
two units—one that is responsible for calculations and the other for handling
user interactions.

Such units of execution are called threads.


The application developer may create multiple threads by partitioning the application into
a number of logical units and creating a thread for each. 2
The essential difference is that while each process has a complete set of its own
variables, threads share the same data.

However, shared variables make communication between threads more efficient and
easier to program than interprocess communication.

Moreover, on some operating systems, threads are more “lightweight” than processes—
it takes less overhead to create and destroy individual threads than it does to launch
new processes

Programs that can run more than one thread at once are said to be multithreaded.

A multithreaded program contains two or more parts that can run concurrently.
Each part of such a program is called a thread, and each thread defines a separate path
of execution

Multithreading is extremely useful in practice. For example,


• A browser should be able to simultaneously download multiple files.
• A web server needs to be able to serve concurrent requests.
3
A principal advantage of multithreading is that it enables you to write very efficient
programs because it lets you utilize the idle time that is present in most programs.

threads can be executed simultaneously in multiprocessor systems or multiple threads


share CPU time, known as time sharing as shown below.

(a) Here multiple threads are running on multiple CPUs. (b) Here multiple threads share
a single CPU.

The operating system is responsible for scheduling and allocating resources to threads

4
Thread State
Threads can be in one of six states:
New Threads
When you create a thread with the new operator—for example, new Thread(r)—the
thread is not yet running. This means that it is in the new state.

When a thread is in the new state, the program has not started executing code inside of
it.

Runnable Threads
Once you invoke the start method, the thread is in the runnable state. A runnable
thread may or may not actually be running. It is up to the operating system to give the
thread time to run.

Once a thread is running, it doesn’t necessarily keep running.


In fact, it is desirable that running threads occasionally pause so that other threads have
a chance to run.

The details of thread scheduling depend on the services that the operating system
provides. 5
Preemptive scheduling systems give each runnable thread a slice of time to perform its
task.
When that slice of time is exhausted, the operating system preempts the thread and
gives another thread an opportunity to work.

When selecting the next thread, the operating system takes into account the thread
priorities.

Blocked Threads
When the thread tries to acquire an intrinsic object lock that is currently held by another
thread, it becomes blocked.

The thread becomes unblocked when all other threads have relinquished the lock and
the thread scheduler has allowed this thread to hold it

Waiting Threads
When the thread waits for another thread to notify the scheduler of a condition, it enters
the waiting state.
In practice, the difference between the blocked and waiting state is not significant. 6
Timed Waiting Threads
Several methods have a timeout parameter.
Calling them causes the thread to enter the timed waiting state.
This state persists either until the timeout expires or the appropriate notification has been
received

Terminated Threads
A thread is terminated for one of two reasons:
• It dies a natural death because the run method exits normally.
• It dies abruptly because an uncaught exception terminates the run method.
In particular, you can kill a thread by invoking its stop method.

That method throws a ThreadDeath error object that kills the thread. However, the stop
method is deprecated, and you should never call it in your own code.

7
Thread states 8
Thread Priorities
With several threads running on a system, you may want to prioritize their execution.
You do so by assigning a priority level to each thread. In Java, the thread priority levels
range from 1 to 10.

The priority 0 is reserved for the virtual machine. Java also provides a few predefined
constants for setting the thread priority.

MAX_PRIORITY designates the top priority of 10,

MIN_PRIORITY designates the minimum priority of 1, and

NORM_PRIORITY specifies the normal priority of 5.

A thread with a higher priority gets the CPU first.

The OS maintains a separate queue for all threads belonging to each priority level

9
Because threads with the higher priority level get the CPU first, this implies that a thread
with a lower priority will starve for CPU cycles forever if the higher-priority threads never
finish.
However, this is not exactly true. The OS periodically raises the priority of these
“starving” threads until they reach the currently executing thread priority level.

At this level, each thread will eventually get its time slice.
10
After the time slice is over, the thread will be returned to the queue of its original priority.
There are two types of threads in Java:
• Daemon threads
• Non-daemon (user) threads

A daemon thread terminates automatically as soon as the parent thread that created this
thread terminates.

You can turn a thread into a daemon thread by calling


t.setDaemon(true);
This method must be called before the thread is started.

A daemon is simply a thread that has no other role in life than to serve others.
Examples are timer threads that send regular “timer ticks” to other threads or threads
that clean up stale cache entries.

A daemon thread should never access a persistent resource such as a file or database
since it can terminate at any time, even in the middle of an operation.

11
A non-daemon thread, however, continues to live even when the parent dies.
As long as there is at least one thread alive, we say that the process is alive.

When all non-daemon threads along with the remaining daemon threads of an
application die, we say that the process is dead.

Every Java application has at least one non-daemon thread, which is the main thread.
When the program quits this thread or the user closes the application, the main thread
dies.

However, the process can continue to live if it has already spawned other non-daemon
threads.

Creating a Thread
Java implements thread functionality by using the following classes/interfaces:
• The interface Runnable
• The class Thread
• The class ThreadGroup
12
The Runnable interface abstracts a unit of executable code. You can construct a thread
on any object that implements the Runnable interface.

Runnable defines only one method called run( ), which is declared like this:

public void run( )

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.

After you have created a class that implements Runnable, you will instantiate an object
of type Thread on an object of that class.
13
For example
All Swing components must be configured from the event dispatch thread, the thread of
control that passes events such as mouse clicks and keystrokes to the user interface
components.
The following code fragment is used to execute statements in the event dispatch thread:

EventQueue.invokeLater(new Runnable()
{

public void run()


{
statements

});

14
The Thread class has several constructors, which take in a variation of the following
parameters:

String name.
Every Thread object has a name associated with it. You can assign a thread any name
you like because the purpose of the name is to allow you to distinguish the various
threads you create.

If you do not assign your threads a name, the Thread class names them Thread0,
Thread1, Thread2, and so on.

Runnable target.
Associates a Runnable object as the target of the Thread.

ThreadGroup.
The group that the thread belongs to

long stackSize.
The number of bytes you want allocated for the size of the stack used by this thread.
15
Here is a simple procedure for running a task in a separate thread:
1. Place the code for the task into the run method of a class that implements the
Runnable interface. That interface is very simple, with a single method:

public interface Runnable

{
void run();

}
You simply implement a class, like this:
class MyRunnable implements Runnable
{
public void run()

task code
}
}
16
2. Construct an object of your class:
Runnable r = new MyRunnable();

3. Construct a Thread object from the Runnable:


Thread t = new Thread(r);

4. Start the thread:


t.start();
Note
You can also define a thread by forming a subclass of the Thread class, like this:
class MyThread extends Thread
{
public void run()
{
task code
}
}
Then you construct an object of the subclass and call its start method. However,
17
this
approach is no longer recommended.
The Thread class defines several methods that help manage threads. Several of those
used in this chapter are shown here:

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.
18
Example Objects of MyThread can
// Create a thread by implementing Runnable. be run in their own threads
class MyThread implements Runnable { because MyThread
String thrdName; implements Runnable.
MyThread(String name) {
thrdName = name;
}
// Entry point of thread.
public void run() { Threads start executing here
System.out.println(thrdName + " starting.");
try {
for(int count=0; count < 10; count++) {
Thread.sleep(200);
System.out.println("In " + thrdName +", count is " + count);
}
}
catch(InterruptedException exc) {
System.out.println(thrdName + " interrupted.");
}
System.out.println(thrdName + " terminating.");
}
19
}
public class UseThreads {
public static void main(String args[]) {
System.out.println("Main thread starting.");
// First, construct a MyThread object.
Create a runnable object.
MyThread mt = new MyThread("Child #1");
// Next, construct a thread from that object.
Construct a thread on that object
Thread newThrd = new Thread(mt);
// Finally, start execution of the thread.
Start running the thread.
newThrd.start();
for(int i=0; i<20; i++) {
System.out.print(".");
try {
Thread.sleep(200);
}
catch(InterruptedException exc) {
System.out.println("Main thread interrupted.");
}
}
System.out.println("Main thread ending.");
}
20
}
The output produced by this program is as follows. Because of differences between computing
environments, the precise output that you see may differ slightly from that shown here:

Caution
Do not call the run method of the Thread class or the Runnable object.
Calling the run method directly merely executes the task in the same thread—no new thread is
started. Instead, call the Thread.start method. It will create a new thread that executes the
21
run method.
It is possible to have a thread begin execution as soon as it is created.

In the case of MyThread, this is done by instantiating a Thread object inside


MyThread’s constructor.

Second, there is no need for MyThread to store the name of the thread since it is
possible to give a name to a thread when it is created.

Here is the improved version of the preceding program:

public class MyImprovedThread implements Runnable {


Thread thrd; A reference to the thread is stored in thrd.

// Construct a new thread.


MyImprovedThread(String name) {
thrd = new Thread(this, name); The thread is named when it is created.

Begin executing the thread


thrd.start(); // start the thread
}
22
// Begin execution of new thread.
public void run() {
System.out.println(thrd.getName() + " starting.");
try {
for(int count=0; count<10; count++) {
Thread.sleep(200);
System.out.println("In " + thrd.getName() +
", count is " + count);
}
}
catch(InterruptedException exc) {
System.out.println(thrd.getName() + " interrupted.");
}
System.out.println(thrd.getName() + " terminating.");
}
}
23
public class UseThreadsImproved {
public static void main(String args[]) {
System.out.println("Main thread starting.");
MyImprovedThread mt = new MyImprovedThread("Child #1");
for(int i=0; i < 20; i++) {
System.out.print(".");
try {
Thread.sleep(200);
}
catch(InterruptedException exc) {
System.out.println("Main thread interrupted.");
}
}
System.out.println("Main thread ending.");
}
}
This version produces the same output as before. Notice that the thread is
stored in thrd inside MyThread. 24
Extending Thread
When a class extends Thread, it 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.
It is possible to override other Thread methods, but doing so is not required.

Example
public class MyThread2 extends Thread {
// Construct a new thread.
MyThread2(String name) {
super(name); // name thread
start(); // start the thread
}
public void run() {
System.out.println(getName() + " starting.");
try {
25
for(int count=0; count < 10; count++) {
Thread.sleep(400);
System.out.println("In " + getName() +
", count is " + count);
}
}
catch(InterruptedException exc) {
System.out.println(getName() + " interrupted.");
}
System.out.println(getName() + " terminating.");
}
}

26
public class ExtendThread {
public static void main(String args[]) {
System.out.println("Main thread starting.");
MyThread2 mt = new MyThread2("Child #1");
for(int i=0; i < 50; i++) {
System.out.print(".");
try {
Thread.sleep(100);
}
catch(InterruptedException exc) {
System.out.println("Main thread interrupted.");
}
}
System.out.println("Main thread ending.");
}
}
27
Sample output

Creating Multiple Threads


The preceding examples have created only one child thread.
However, your program can spawn as many threads as it needs.
For example, the following program creates three child threads:

28
// Create multiple threads.
public class MyThread3 implements Runnable {
Thread thrd;
// Construct a new thread.
MyThread3(String name) {
thrd = new Thread(this, name);
thrd.start(); // start the thread
}
// Begin execution of new thread.
public void run() {
System.out.println(thrd.getName() + " starting.");
try {
for(int count=0; count < 10; count++) {
Thread.sleep(400);
System.out.println("In " + thrd.getName() +
", count is " + count);
}
}
catch(InterruptedException exc) {
System.out.println(thrd.getName() + " interrupted.");
}
System.out.println(thrd.getName() + " terminating.");
29
}}
public class MoreThreads {
public static void main(String args[]) {
System.out.println("Main thread starting.");
MyThread3 mt1 = new MyThread3("Child #1");
Create and start
MyThread3 mt2 = new MyThread3("Child #2"); executing three threads.
MyThread3 mt3 = new MyThread3("Child #3");
for(int i=0; i < 50; i++) {
System.out.print(".");
try {
Thread.sleep(100);
}
catch(InterruptedException exc) {
System.out.println("Main thread interrupted.");
}
}
System.out.println("Main thread ending.");
}
}

30
Sample output
As you can see, once started, all three
child threads share the CPU.

Notice that the threads are started in the


order in which they are created.

However, this may not always be the


case. Java is free to schedule the
execution of threads in its own way.

Of course, because of differences in


timing or environment, the precise output
from the program may differ, so don’t be
surprised if you see slightly different
results when you try the program.

31
Determining When a Thread Ends
Thread provides two means by which you can determine if a thread has ended. First,
you can call isAlive( ) on the thread. 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.

To try isAlive( ), substitute this version of MoreThreads for the one shown in the
preceding program:

Example

32
public class MoreThreads4 {
public static void main(String args[]) {
System.out.println("Main thread starting.");
MyThread3 mt1 = new MyThread3("Child #1");
MyThread3 mt2 = new MyThread3("Child #2");
MyThread3 mt3 = new MyThread3("Child #3");
do {
System.out.print(".");
try {
Thread.sleep(100);
}
catch(InterruptedException exc) {
System.out.println("Main thread interrupted.");
}
} while (mt1.thrd.isAlive() ||
This waits until all threads
mt2.thrd.isAlive() || terminate
mt3.thrd.isAlive());
System.out.println("Main thread ending.");
}
33
}
This version produces output that is similar to the previous version, except that main( )
ends as soon as the other threads finish.

The difference is that it uses isAlive( ) to wait for the child threads to terminate.
Another way to wait for a thread to finish is to call join( ), shown here:
final void join( ) throws InterruptedException

This method waits until the thread on which it is called terminates.


Its name comes from the concept of the calling thread waiting until the specified thread
joins it.

Additional forms of join( ) allow you to specify a maximum amount of time that you
want to wait for the specified thread to terminate.

Here is a program that uses join( ) to ensure that the main thread is the last to stop:

34
// Use join().
public class MyThreadJoin implements Runnable {
Thread thrd;
// Construct a new thread.
MyThreadJoin(String name) {
thrd = new Thread(this, name);
thrd.start(); // start the thread
}
// Begin execution of new thread.
@Override
public void run() {
System.out.println(thrd.getName() + " starting.");
try {
for(int count=0; count < 10; count++) {
Thread.sleep(400);
System.out.println("In " + thrd.getName() +
", count is " + count);
} 35
}
catch(InterruptedException exc) {
System.out.println(thrd.getName() + " interrupted.");
}
System.out.println(thrd.getName() + " terminating.");
}
}

public class JoinThreads {


public static void main(String args[]) {
System.out.println("Main thread starting.");
MyThreadJoin mt1 = new MyThreadJoin("Child #1");
MyThreadJoin mt2 = new MyThreadJoin("Child #2");
MyThreadJoin mt3 = new MyThreadJoin("Child #3");
try {
mt1.thrd.join(); Wait until the specified thread ends.
System.out.println("Child #1 joined.");
} 36
mt2.thrd.join(); Wait until the specified thread ends.
System.out.println("Child #2 joined.");
mt3.thrd.join(); Wait until the specified thread ends.
System.out.println("Child #3 joined.");
}
catch(InterruptedException exc) {
System.out.println("Main thread interrupted.");
}
System.out.println("Main thread ending.");
}
}

In general,
The join method allows one thread to wait for the completion of another. If t is a Thread
object whose thread is currently executing,

t.join();
causes the current thread to pause execution until t's thread terminates.
37
Sample output from this program is shown here. Remember that when you try the
program, your precise output may vary slightly.

As you can see, after the calls to join( ) return,


the threads have stopped executing.

38
Thread Priorities (revisited…)
A thread’s priority determines how much CPU time a thread receives relative to the other
active threads.

In general, over a given period of time, low-priority threads receive little. High-priority
threads receive a lot.

It is important to understand that factors other than a thread’s priority also affect how
much CPU time a thread receives.

For example, if a high-priority thread is waiting on some resource, perhaps for keyboard
input, then it will be blocked, and a lower priority thread will run.

However, when that high-priority thread gains access to the resource, it can preempt the
low-priority thread and resume execution.
Another factor that affects the scheduling of threads is the way the operating system
implements multitasking.

Thus, just because you give one thread a high priority and another a low priority does
not necessarily mean that one thread will run faster or more often than the other. It’s just
39
that the high-priority thread has greater potential access to the CPU.
You can change a thread’s priority by calling setPriority( ), which is a member of
Thread. This is its general form:

final void setPriority(int level)

You can obtain the current priority setting by calling the getPriority( ) method of
Thread, shown here:
final int getPriority( )

Example
// Demonstrate thread priorities.
public class Priority implements Runnable {
int count;
Thread thrd;
static boolean stop = false;
static String currentName;
/* Construct a new thread. Notice that this
constructor does not actually start the
40
threads running. */
Priority(String name) {
thrd = new Thread(this, name);
count = 0;
currentName = name;
}
// Begin execution of new thread.
public void run() {
System.out.println(thrd.getName() + " starting.");
do {
count++;
if(currentName.compareTo(thrd.getName()) != 0) {
currentName = thrd.getName();
System.out.println("In " + currentName);
} The first thread to
} while(stop == false && count < 100); 100 stops
stop = true; all threads.
System.out.println("\n" + thrd.getName() +
" terminating.");
41
}}
class PriorityDemo {
public static void main(String args[]) {
Priority mt1 = new Priority("High Priority");
Priority mt2 = new Priority("Low Priority");
// set the priorities Give mt1 a
mt1.thrd.setPriority(Thread.NORM_PRIORITY+2); higher
mt2.thrd.setPriority(Thread.NORM_PRIORITY-2); priority
than mt2.
// start the threads
mt1.thrd.start();
mt2.thrd.start();
try {
mt1.thrd.join();
mt2.thrd.join();
}
catch(InterruptedException exc) {
System.out.println("Main thread interrupted.");
}
System.out.println("\nHigh priority thread counted to " +
42
mt1.count);
System.out.println("Low priority thread counted to " +
mt2.count);
}
}}
Here is a sample run for 100 loops

In this run, the high-priority thread got a vast majority of the CPU time.

Of course, the exact output produced by this program will depend upon the speed of your
CPU, the number of CPUs in your system, the operating system you are using, and the
number of other tasks running in the system. 43
Synchronization
When using multiple threads, it is sometimes necessary to coordinate the activities of two
or more.

The process by which this is achieved is called synchronization.

The most common reason for synchronization is when two or more threads need access to a
shared resource that can be used by only one thread at a time.

For example, when one thread is writing to a file, a second thread must be prevented from
doing so at the same time.

What happens if two threads have access to the same object and each calls a method that
modifies the state of the object?

Depending on the order in which the data were accessed, corrupted objects can result. Such
a situation is often called a race condition.

Key to synchronization in Java is the concept of the monitor, which controls access to an
object.

44
A monitor works by implementing the concept of a lock.
When an object is locked by one thread, no other thread can gain access to the object.

When the thread exits, the object is unlocked and is available for use by another thread.

There are two ways that you can synchronize your code.

a. Using Synchronized Methods


You can synchronize access to a method by modifying it with the synchronized
keyword.

When that method is called, the calling thread enters the object’s monitor, which then
locks the object.
While locked, no other thread can enter the method, or enter any other synchronized
method defined by the object’s class.

When the thread returns from the method, the monitor unlocks the object, allowing it to
be used by the next thread.
45
Example
// Use synchronize to control access.
public class SumArray {
private int sum;
synchronized int sumArray(int nums[]) {
sum = 0; // reset sum
for(int i=0; i<nums.length; i++) {
sum += nums[i];
System.out.println("Running total for " +
Thread.currentThread().getName() +" is " + sum);
try {
Thread.sleep(10); // allow task-switch
}
catch(InterruptedException exc) {
System.out.println("Thread interrupted.");
}
}
return sum;
}
}
46
public class MyThreadArray implements Runnable {
Thread thrd;
static SumArray sa = new SumArray();
int a[];
int answer;
// Construct a new thread.
MyThreadArray(String name, int nums[]) {
thrd = new Thread(this, name);
a = nums;
thrd.start(); // start the thread
}
// Begin execution of new thread.
public void run() {
System.out.println(thrd.getName() + " starting.");
answer = sa.sumArray(a);
System.out.println("Sum for " + thrd.getName() +
" is " + answer);
System.out.println(thrd.getName() + " terminating.");
}
}
47
public class Sync {
public static void main(String args[]) {
int a[] = {1, 2, 3, 4, 5};
MyThreadArray mt1 = new MyThreadArray("Child #1", a);
MyThreadArray mt2 = new MyThreadArray("Child #2", a);
try {
mt1.thrd.join();
mt2.thrd.join();
}
catch(InterruptedException exc) {
System.out.println("Main thread interrupted.");
}
}
}
48
Sample output (The precise output may differ on your computer.)

49
b. The synchronized Statement
Although creating synchronized methods within classes that you create is an easy and
effective means of achieving synchronization, it will not work in all cases.

For example, you might want to synchronize access to some method that is not modified
by synchronized.

This can occur because you want to use a class that was not created by you but by a
third party, and you do not have access to the source code.

Thus, it is not possible for you to 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.

This is the general form of a synchronized block:


synchronized(objref) {
// statements to be synchronized
} 50
Here, objref is a reference to the object being synchronized.

Once a synchronized block has been entered, no other thread can call a synchronized
method on the object referred to by objref until the block has been exited.

For example, another way to synchronize calls to sumArray( ) is to call it from within
a synchronized block, as shown in this version of the program:

// Use a synchronized block to control access to SumArray.


class SumArray {
private int sum;
Here, sumArray( )
int sumArray(int nums[]) {
is not synchronized.
sum = 0; // reset sum
for(int i=0; i<nums.length; i++) {
sum += nums[i];
System.out.println("Running total for " +
Thread.currentThread().getName() + " is " + sum);
try {
Thread.sleep(10); // allow task-switch
}
catch(InterruptedException exc) {
System.out.println("Thread interrupted.");
}
}
return sum; 51
}
class MyThread implements Runnable {
Thread thrd;
static SumArray sa = new SumArray();
int a[];
int answer;
// Construct a new thread.
MyThread(String name, int nums[]) {
thrd = new Thread(this, name);
a = nums;
thrd.start(); // start the thread
}
// Begin execution of new thread.
public void run() {
int sum;
System.out.println(thrd.getName() + " starting.");
// synchronize calls to sumArray()
synchronized(sa) { Here, calls to sumArray( )
answer = sa.sumArray(a); on sa are synchronized..
}
System.out.println("Sum for " + thrd.getName() +
" is " + answer);
System.out.println(thrd.getName() + " terminating.");
}
52
class Sync {
public static void main(String args[]) {
int a[] = {1, 2, 3, 4, 5};
MyThread mt1 = new MyThread("Child #1", a);
MyThread mt2 = new MyThread("Child #2", a);
try {
mt1.thrd.join();
mt2.thrd.join();
} catch(InterruptedException exc) {
System.out.println("Main thread interrupted.");
}
}
}

This version produces the same, correct output as the one shown earlier that uses a
synchronized method.
One of the most exciting features of the concurrent API (which are packaged in
java.util.concurrent) is the Fork/Join Framework.
The Fork/Join Framework supports what is often termed parallel programming
(computers that contain two or more processors, including multicore systems).
The concurrency utilities in general, and the Fork/Join Framework specifically, are
features that you will want to explore after you have become more experienced with
multithreading.
53
Thread Communication Using notify( ), wait( ), and notifyAll( )

Java supports interthread communication with the wait( ), notify( ), and


notifyAll( ) methods.

The wait( ), notify( ), and notifyAll( ) methods are part of all objects
because they are implemented by the Object class.

These methods should be called only from within a synchronized context.

Here is how they are used.

When a thread is temporarily blocked from running, it calls wait( ).

This causes the thread to go to sleep and the monitor for that object to be released,
allowing another thread to use the object.

At a later point, the sleeping thread is awakened when some other thread enters the
same monitor and calls notify( ), or notifyAll( ).

54
Following are the various forms of wait( ) defined by Object:

final void wait( ) throws InterruptedException


waits until notified

final void wait(long millis) throws InterruptedException


waits until notified or until the specified period of milliseconds has expired

final void wait(long millis, int nanos) throws InterruptedException


allows you to specify the wait period in terms of nanoseconds.

Here are the general forms for notify( ) and notifyAll( ):


final void notify( )
final void notifyAll( )

A call to notify( ) resumes one waiting thread.

A call to notifyAll( ) notifies all threads, with the highest priority thread gaining
access to the object.

Although wait( ) normally waits until notify( ) or notifyAll( ) is called, there is a


possibility that in very rare cases the waiting thread could be awakened due to a
55
spurious wakeup.
Example
// Use wait() and notify() to create a ticking clock.
class TickTock {
String state; // contains the state of the clock
synchronized void tick(boolean running) {
if(!running) { // stop the clock
state = "ticked";
notify(); // notify any waiting threads
return;
}
System.out.print("Tick ");
state = "ticked"; // set the current state to ticked
notify(); // let tock() run
try {
while(!state.equals("tocked"))
wait(); // wait for tock() to complete
}
catch(InterruptedException exc) {
System.out.println("Thread interrupted.");
}
}

56
synchronized void tock(boolean running) {
if(!running) { // stop the clock
state = "tocked";
notify(); // notify any waiting threads
return;
}
System.out.println("Tock");
state = "tocked"; // set the current state to tocked
notify(); // let tick() run
try {
while(!state.equals("ticked"))
wait(); // wait for tick to complete
}
catch(InterruptedException exc) {
System.out.println("Thread interrupted.");
}
}
}

57
class MyClockThread implements Runnable {
Thread thrd;
TickTock ttOb;
// Construct a new thread.
MyClockThread(String name, TickTock tt) {
thrd = new Thread(this, name);
ttOb = tt;
thrd.start(); // start the thread
}
// Begin execution of new thread.
public void run() {
if(thrd.getName().compareTo("Tick") == 0) {
for(int i=0; i<5; i++)
ttOb.tick(true);
ttOb.tick(false);
}
else {
for(int i=0; i<5; i++)
ttOb.tock(true);
ttOb.tock(false);
}
}
}
58
public class ThreadCom {
public static void main(String args[]) {
TickTock tt = new TickTock();
MyClockThread mt1 = new MyClockThread("Tick", tt);
MyClockThread mt2 = new MyClockThread("Tock", tt);
try {
mt1.thrd.join();
mt2.thrd.join();
} catch(InterruptedException exc) {
System.out.println("Main thread interrupted.");
}
}
}

Here is the output produced by the program:


Tick Tock
Tick Tock
Tick Tock
Tick Tock
Tick Tock
Note:
Always invoke wait inside a loop that tests for the condition being waited for.
Don't assume that the interrupt was for the particular condition you were waiting for, or
that the condition is still true. 59
Q:

I have heard the term deadlock applied to misbehaving multithreaded programs. What is
it, and how can I avoid it? Also, what is a race condition, and how can I avoid that, too?

A:
Deadlock is, as the name implies, a situation in which one thread is waiting for another
thread to do something, but that other thread is waiting on the first.

Thus, both threads are suspended, waiting on each other, and neither executes.

The cause of the deadlock often is not readily understood just by looking at the source
code to the program because concurrently executing threads can interact in complex
ways at run time.

To avoid deadlock, careful programming and thorough testing is required. Remember, if


a multithreaded program occasionally “hangs,” deadlock is the likely cause.

A race condition occurs when two (or more) threads attempt to access a shared
resource at the same time, without proper synchronization.

For example, one thread may be writing a new value to a variable while another thread
is incrementing the variable’s current value. 60
Without synchronization, the new value of the variable will depend upon the order in
which the threads execute.

(Does the second thread increment the original value or the new value written by the first
thread?)

In situations like this, the two threads are said to be “racing each other,” with the final
outcome determined by which thread finishes first.

Like deadlock, a race condition can occur in difficult-to-discover ways.


The solution is prevention: careful programming that properly synchronizes access to
shared resources.

Suspending, Resuming, and Stopping Threads


The mechanisms to suspend, stop, and resume threads differ between early versions
of Java and more 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.

They have the following forms

61
final void resume( )
final void suspend( )
final void stop( )

While these methods seem to be a perfectly reasonable and convenient approach to


managing the execution of threads, they must no longer be used. Here’s why.

The suspend( ) method of the Thread class was deprecated by Java 2.

This was done because suspend( ) can sometimes cause serious problems that
involve deadlock.

The resume( ) method is also deprecated. It does not cause problems but cannot be
used without the suspend( ) method as its counterpart.

The stop( ) method of the Thread class was also deprecated by Java 2. This was
done because this method too can sometimes cause serious problems.

Typically, Suspending, Resuming, and Stopping Threads is accomplished by


establishing two flag variables:

one for suspend and resume, and one for stop.


62
For suspend and resume, as long as the flag is set to “running,” the run( ) method
must continue to let the thread execute.

If this variable is set to “suspend,” the thread must pause.

For the stop flag, if it is set to “stop,” the thread must terminate.

The following example shows one way to implement your own versions of suspend( ),
resume( ), and stop( ):

// Suspending, resuming, and stopping a thread.


public class MySRSThread implements Runnable {
Thread thrd;
boolean suspended; Suspends thread when true.
boolean stopped; Stops thread when true.
MySRSThread(String name) {
thrd = new Thread(this, name);
suspended = false;
stopped = false;
thrd.start();
}

63
// This is the entry point for thread.
public void run() {
System.out.println(thrd.getName() + " starting.");
try {
for(int i = 1; i < 1000; i++) {
System.out.print(i + " ");
if((i%10)==0) {
System.out.println();
Thread.sleep(250);
}
// Use synchronized block to check suspended and stopped.
synchronized(this) {
while(suspended) {
wait();
}
if(stopped) This synchronized block checks
break; suspended and stopped.
}
}
} catch (InterruptedException exc) {
System.out.println(thrd.getName() + " interrupted.");
}
System.out.println(thrd.getName() + " exiting.");
} 64
// Stop the thread.
synchronized void mystop() {
stopped = true;
// The following ensures that a suspended thread can be
stopped.
suspended = false;
notify();
}
// Suspend the thread.
synchronized void mysuspend() {
suspended = true;
}
// Resume the thread.
synchronized void myresume() {
suspended = false;
notify();
}
}

65
class Suspend {
public static void main(String args[]) {
MySRSThread ob1 = new MySRSThread("My Thread");
try {
Thread.sleep(1000); // let ob1 thread start executing
ob1.mysuspend();
System.out.println("Suspending thread.");
Thread.sleep(1000);
ob1.myresume();
System.out.println("Resuming thread.");
Thread.sleep(1000);
ob1.mysuspend();
System.out.println("Suspending thread.");
Thread.sleep(1000);
ob1.myresume();
System.out.println("Resuming thread.");
Thread.sleep(1000);
ob1.mysuspend();
System.out.println("Stopping thread.");
ob1.mystop();
} catch (InterruptedException e) {
System.out.println("Main thread Interrupted");
}
66
// wait for thread to finish
try {
ob1.thrd.join();
} catch (InterruptedException e) {
System.out.println("Main thread Interrupted");
}
System.out.println("Main thread exiting.");
}
}
Example
My Thread starting.
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
31 32 33 34 35 36 37 38 39 40
Suspending thread.
Resuming thread.
41 42 43 44 45 46 47 48 49 50
51 52 53 54 55 56 57 58 59 60
61 62 63 64 65 66 67 68 69 70
71 72 73 74 75 76 77 78 79 80
Suspending thread.
Resuming thread.
81 82 83 84 85 86 87 88 89 90 67
91 92 93 94 95 96 97 98 99 100
101 102 103 104 105 106 107 108 109 110
111 112 113 114 115 116 117 118 119 120
Stopping thread.
My Thread exiting.
Main thread exiting.

Deadlock
• Deadlock describes a situation where two or more threads are blocked forever,
waiting for each other.
• This is like when two people are standing at a door, both holding it open for the other
to pass through.

See the image below Rsrc


A

Thread Thread
2 1

Rsrc
B

Thread 1 has Rsrc A and is requesting for Rsrc B


Thread 2 has Rsrc B and is requesting for Rsrc A 68
Deadlock situation in multithreaded java

In this figure, the two threads perform synchronized access to their code. Consider the
situation where Thread 1 obtains a lock on Object A and then loses the CPU. Now
Thread 2, which gains the CPU, obtains a lock on Object B and executes a few code
lines before losing the CPU. Thread 1, which now has the CPU, proceeds with its code
to obtain a lock on Object B. However, because Object B has been locked by Thread 2,
it cannot proceed and has to wait until the lock on Object B is released by Thread 2.
Now, Thread 2 gains the CPU one more time and proceeds with its own code, in which it
tries to obtain a lock on Object A. Because Object A has previously been locked by
Thread 1, Thread 2 cannot continue unless the lock on Object A is released by Thread 1.
You can see that each thread must wait on the other to release its lock and therefore
neither can proceed. This situation is called deadlock.

69
Example
package com.assignment;
/**
*
* @author Administrator
*/
//main class
public class ThreadWithDeadlock {
public static Object Lock1 = new Object();
public static Object Lock2 = new Object();

public static void main(String args[]) {


ThreadDemo1 T1 = new ThreadDemo1();
ThreadDemo2 T2 = new ThreadDemo2();
T1.start();
T2.start();
}

70
private static class ThreadDemo1 extends Thread {
public void run() {
synchronized (Lock1) {
System.out.println("Thread 1: Holding lock 1...");

try { Thread.sleep(10); }
catch (InterruptedException e) {}
System.out.println("Thread 1: Waiting for lock 2...");

synchronized (Lock2) {
System.out.println("Thread 1: Holding lock 1 & 2...");
}
}
}
}

71
private static class ThreadDemo2 extends Thread {
public void run() {
synchronized (Lock2) {
System.out.println("Thread 2: Holding lock 2...");

try { Thread.sleep(10); }
catch (InterruptedException e) {}
System.out.println("Thread 2: Waiting for lock 1...");

synchronized (Lock1) {
System.out.println("Thread 2: Holding lock 1 & 2...");
}
}
}
}
}
When you compile and execute the
above program, you find a
deadlock situation and following is
the output produced by the
program
72
The above program will hang forever because neither of the threads in position to
proceed and waiting for each other to release the lock, so you can come out of the
program by pressing CTRL+C.

Solutions to Deadlock
Deadlocks have no simple solution. They can be avoided only by careful
coding. However, three techniques can be used to help you in detecting and
preventing deadlocks in your code:

Lock Ordering
• In our earlier example, deadlock can be avoided by maintaining the order in
which the locks are obtained and released by two threads.

• If locks on multiple objects are obtained and released in the same order by
both threads, a deadlock cannot occur.
• Thus, if both threads in our example obtain the locks in the order Object A
first, followed by Object B, a deadlock will not occur.

• The general rule here is that in case of multiple locks, if all locks are always
taken in the same order by any thread, deadlocks cannot occur.
73
Example ( A solution to the previous example)
Let's change the order of the lock and run of the same program to see if both
the threads still wait for each other

package com.assignment;

/**
*
* @author Administrator
*/
public class ThreadWithoutDeadlock {
public static Object Lock1 = new Object();
public static Object Lock2 = new Object();

public static void main(String args[]) {


ThreadDemo1 T1 = new ThreadDemo1();
ThreadDemo2 T2 = new ThreadDemo2();
T1.start();
T2.start();

}
74
private static class ThreadDemo1 extends Thread {
public void run() {
synchronized (Lock1) {
System.out.println("Thread 1: Holding lock 1...");

try {
Thread.sleep(10);
}catch (InterruptedException e) {}
System.out.println("Thread 1: Waiting for lock 2...");

synchronized (Lock2) {
System.out.println("Thread 1: Holding lock 1 & 2...");
}
}
}
}

75
private static class ThreadDemo2 extends Thread {
public void run() {
synchronized (Lock1) {
System.out.println("Thread 2: Holding lock 1...");

try {
Thread.sleep(10);
}catch (InterruptedException e) {}
System.out.println("Thread 2: Waiting for lock 2...");

synchronized (Lock2) {
System.out.println("Thread 2: Holding lock 1 & 2...");
}
}
}
}
} So just changing the order of the
locks prevent the program in going
into a deadlock situation and
completes with the following result

76
Lock Timeout
• Putting a timeout on lock attempts helps in preventing a deadlock situation.

• If a thread does not succeed in obtaining all the necessary locks within the
given timeout period, it will back up, freeing all locks taken so far. It then
waits for a random amount of time before making another attempt to obtain
the locks.

• The synchronized keyword has no facility to specify this timeout.

• To use a timeout facility, use the java.util.concurrent.locks.Lock


interface introduced in J2SE 5.0 for thread synchronization.

The ReentrantLock class in the same package implements this interface.


The use of this class is illustrated in the following code snippet:

Lock lock = new ReentrantLock();


lock.lock();
// critical section
lock.unlock();
77
You may use the tryLock (long timeout, TimeUnit timeUnit)
method to specify the timeout for attempts to acquire a lock.

Deadlock Detection
• If both these remedies of lock ordering and timeout are not feasible, we can
resort to deadlock detection, which is definitely a more difficult solution of
deadlock prevention.

• In this solution, we record every request and acquire a lock by all threads.

• Generally, this is stored in a map or graph for ease of traversal. When a


request for a lock is denied, the thread traverses this lock graph to check for
deadlocks.

78

You might also like