0% found this document useful (0 votes)
5 views50 pages

Java Multithreading Concepts Explained

The document discusses multithreaded programming, particularly in the context of Java, explaining the distinction between processes and threads, with threads being lighter and sharing the same address space. It details the Java thread model, the lifecycle of a thread, and methods for creating threads either by extending the Thread class or implementing the Runnable interface. Additionally, it provides examples of thread creation and management, highlighting the advantages of multithreading in maximizing resource utilization.

Uploaded by

ankithahp13
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
5 views50 pages

Java Multithreading Concepts Explained

The document discusses multithreaded programming, particularly in the context of Java, explaining the distinction between processes and threads, with threads being lighter and sharing the same address space. It details the Java thread model, the lifecycle of a thread, and methods for creating threads either by extending the Thread class or implementing the Runnable interface. Additionally, it provides examples of thread creation and management, highlighting the advantages of multithreading in maximizing resource utilization.

Uploaded by

ankithahp13
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

14.

Multithreaded Programming

14.1 Introduction

We are familiar with the fact that the modern operating systems like Windows XP, Unix
and others support multitasking facility, wherein multiple tasks are executed
simultaneously and it results in increased degree of utilization of system resources like
CPU, memory and I/O devices, and also increases the speed of execution of programs. A
task is also called a process and it is nothing but a program in the status of execution.
Each process has its own address space and has to have access to the other system
resources and thus it is called a heavy weight process. The process schedulers of the
operating systems take the responsibility of assigning the system resources to each
process in an interleaved fashion so that all the processes will get their turn to get
executed. Even though it is a known fact that only one process can be in the state of
execution (in a uniprocessor system), because of the ability of the task scheduler to
switch the attention of the resources from one process to another process and the high
speed of the CPU and other resources of the system being used, we will be under the
illusion that all the processes are executed simultaneously.
We learnt that a process is a program in the status of execution and the modern
operating systems can dispatch processes as the smallest executable units and execute
multiple processes simultaneously. Here a program or a process is viewed as a single
flow of control. It is possible that even a program may also have more than one flow of
control and they may be independent. Each such flow of control within a program is
called a thread. Since it does not have its own address space, but shares the address space
of its program, it is called a light weight process. We can even think of executing more
than one thread in a single program simultaneously, thereby further increasing the speed
of execution of the program and taking the degree of resources’ utilization to further
higher level. The phenomenon of execution of multiple threads simultaneously is termed
Multithreading.
Multitasking
An operating system concept
A process is the smallest executable unit
Each process has its own address space and hence called heavy weight process
Context switching is costly

Multithreading
A language feature
A thread is the smallest executable unit
Threads do not have their own address space. They share the address space of the
program of they are the parts and they are called light weight processes
Context switch is inexpensive

As said earlier, a thread is defined to be a flow of control in a program. It is normally


likely that in a program, we may have multiple flows of control, which may be
independent and they are the candidates to be the threads to be executed concurrently.
The java language provides the facility of creating multiple threads in a program and their
execution concurrently; and this is one of the major distinctive features of java language.

14.2 The Java Thread Model

The single threaded systems use a model called an event loop with polling. In this model,
the single thread runs in an infinite loop, polling for a single event queue to decide as to
what to do next. Once this polling mechanism returns with, say, a signal that a file is
ready to be read or that a user input is ready for processing, then the event loop
dispatches control to the appropriate event handler. Until this event handler returns,
nothing else can happen in the system. To be precise, when a thread blocks for want of
some resource the entire program stops running. This results in the wastage of valuable
CPU time.
The java thread model alleviates the problem encountered above by eliminating
the model of event loop with polling. At the root of the java runtime system itself are the
threads. All the built-in class libraries are designed with multithreading in mind. The java
multithreading model allows animation loops to sleep for a second between each frame
without causing the whole system to pause. When a thread blocks in the java program,
only that thread pauses. All other threads continue to get executed by the CPU. As a
result, the degree of the CPU and other system resources’ utilization is maximized

14.3 The Life Cycle of a Thread

The life cycle of a thread involves five states. They are the following:

1. New State
2. Ready State
3. Running State
4. Waiting State
5. Dead State

Once a thread is created it gets into the new state. From the new state, the thread moves to ready
state on invoking the start() method of the Thread class on it. From the ready state, it
moves to running state once it starts executing. From the running state, the thread may take one
of the three paths: 1) it may move to dead state on completion of its execution or 2) it may move
back to ready state to make way for some other thread to run or 3) It may move to waiting state
for want of some resources. From the waiting state, the thread moves back to ready state once it
acquires the required resources. The complete state transition diagram for the states of a thread is
given below.

Dead

New Ready Running Waiting

Life Cycle of a Thread

14.4 The Thread Class

The built-in class by name Thread is central to implement the threading model of java. It is
using this class, user-defined threads are created in the programming environment. In fact, this is
one way of creating the threads. The Thread class has the following methods, which can be
used to get the details of threads.

CurrentThread()
getName()
getPriority()
isAlive()
setPriority()
join()

The currentThread() returns a reference to the current thread object. The syntax of the
method is as follows:

public static Thread currentThread()

The getName() returns the name of the current thread.. The syntax of the method is as follows:

public string getName()

The getPriority() returns the priority of the current thread. The syntax of the method is as
follows:

public int getPriority()

The isAlive() returns a Boolean value indicating whether the current thread is alive or not.
The Syntax of the method is follows:

public boolean isAlive()

The setPriority()is to set the priority level to a thread. The syntax of the method is as
follows:

public void setPriority()

Program-14.1 To illustrate Thread Class

class thread extends Thread


{
public void run()
{
for(int i = 1; i <= 5; i++)
[Link]("thread i = " + i);
}
}

class ThreadMethods
{
public static void main(String s[]) throws
InterruptedException
{

String tname = [Link]().getName();


[Link]("Main Thread Name = " + tname);

boolean isalive = [Link]().isAlive();


[Link]("Main thread is alive = " + isalive);

int priority = [Link]().getPriority();


[Link]("The priority of the main thread = " +
priority);

thread t = new thread();


[Link]("ThreadA");
tname = [Link]();
[Link]("Name of the thread t = " + tname);

[Link](Thread.MIN_PRIORITY);
priority = [Link]();
[Link]("The priority of t = " + priority);

[Link]();
[Link]("Making the thread t sleep for 1
second");
[Link](1000);

isalive = [Link]();
[Link]("Thread is alive = " + isalive);

try
{
[Link]();
}

catch(Exception e)
{
}

[Link]("Main thread exits");


}
}

Input-Output:

Main Thread Name = main


Main thread is alive = true
The priority of the main thread = 5
Name of the thread t = ThreadA
The priority of t = 1
Making the thread t sleep for 1 second
thread i = 1
thread i = 2
thread i = 3
thread i = 4
thread i = 5
Thread is alive = false
Main thread exits

14.5 The Main Thread

When a java program is launched for execution, the main thread of the program starts
executing. The following program retrieves the details like, the thread name, whether the
thread is alive or not, of the main thread

Program-14.2 To illustrate Main Thread

class MainThread
{
public static void main(String s[])
{

Thread t;
t = [Link]();
String tname = [Link]();
[Link]("The name of the Main Thread " +
tname);

boolean isalive = [Link]();


[Link]("The Main Thread is alive = "
+isalive);

[Link]("The Main thread now exits");

}
}

Input-Output

The name of the Main Thread main


The Main Thread is alive = true
The Main thread now exits

14.6 Creating Our Own Threads


We can create our own threads depending on our application requirements. Java provides
two ways of accomplishing it. They are 1) By extending the Thread class and 2) By
implementing the Runnable interface.

1) Extending the Thread Class

The syntax of creating a thread by extending the Thread class is as follows:

class User_Thread extends Thread


{
public void run()
{
//code
}

Note here that User_Thread is the name of the new thread class. It is extending the Thread
class. The User_Thread class provides definition for the run() of the Thread class. The
code in the run() determines the job of the thread. The run() of the thread gets executed on
invocation of the method start() with an object of the class User_Thread. The following
program illustrates creation of a thread by extending the Thread class.

Program-14.3 To illustrate using the Thread class to create threads

class ThreadA extends Thread


{
public void run()
{
for(int i = 1; i <= 5; i++)
[Link]("thread i = " + i);
}
}

class ThreadDemo1
{
public static void main(String s[]) throws
InterruptedException
{
ThreadA t = new ThreadA();

[Link]("ThreadA");
String tname = [Link]();
[Link]("Name of the thread t = " + tname);

int priority = [Link]();


[Link]("The priority of t = " + priority);

[Link]();
[Link]("Making the thread t sleep for 1
second");
[Link](1000);

boolean isalive = [Link]();


[Link]("Thread is alive = " + isalive);
}
}

Input-Output:

Name of the thread t = ThreadA


The priority of t = 5
Making the thread t sleep for 1 second
thread i = 1
thread i = 2
thread i = 3
thread i = 4
thread i = 5
Thread is alive = false

The class ThreadA is extended from the Thread class. The body of the run() in the class has
a loop iterating five times and it displays the values from 1 to 5 on the screen.
In the main() of the ThreadDemo1 class, an object of ThreadA type is created with
its reference in the variable t with the statement ThreadA t = new ThreadA(); . The
statement [Link]("ThreadA"); sets the string “ThreadA” as the name of the thread.
The name of the thread is read into the variable tname with the statement String tname =
[Link]();. The priority of the thread is retrieved into the variable priority with the
statement int priority = [Link](); and it is displayed. Then the thread is
started with the statement [Link]();. The thread is also made to sleep for 1000 milliseconds
with the statement [Link](1000);. The information about whether the thread is alive or not,
is then retrieved and it is also displayed.

2) Implementing Runnable Interface

The syntax of creating a thread by implementing the Runnable interface is as follows:

class User_Thread implements Runnable


{
public void run()
{
//code
}
}

Note here that User_Thread is the name of the new thread class. It is implementing the
Runnable interface. The User_Thread class provides the implementation for the run() of
the Runnable interface. The code in the run() determines the job of the thread. The run()
of the thread gets executed on invocation of the method start() with an object of the class
Thread created through an object of User_Thread type. The following program illustrates
creation of a thread by implementing the Runnable interface.

Program-14.4 To illustrate using Runnable interface to create threads

class ThreadB implements Runnable


{
public void run()
{
for(int i = 1; i <= 5; i++)
[Link]("ThreadB i = " + i);
}
}

class ThreadDemo2
{
public static void main(String s[]) throws
InterruptedException
{
ThreadB b = new ThreadB();
Thread t = new Thread(b);

[Link]("ThreadB");
String tname = [Link]();
[Link]("Name of the thread t = " + tname);

int priority = [Link]();


[Link]("The priority of t = " + priority);

[Link]();
[Link]("Making the thread t sleep for 1
second");
[Link](1000);

boolean isalive = [Link]();


[Link]("ThreadB is alive = " + isalive);
}
}
Input-Output:

Name of the thread t = ThreadB


The priority of t = 5
Making the thread t sleep for 1 second
ThreadB i = 1
ThreadB i = 2
ThreadB i = 3
ThreadB i = 4
ThreadB i = 5
ThreadB is alive = false

The class ThreadB is implementing the Runnable interface. The body of the run() in the class
has a loop iterating five times and it displays the values from 1 to 5 on the screen.
In the main() of the ThreadDemo2 class, an object of ThreadB type is created with
its reference in the variable t with the statement ThreadB b = new ThreadB(); . An
object of the Thread class is created through the object b of ThreadB class with the statement
Thread t = new Thread(b);

The statement [Link]("ThreadB"); sets the string “ThreadB” as the name of the
thread. The name of the thread is read into the variable tname with the statement String
tname = [Link]();. The priority of the thread is retrieved into the variable priority
with the statement int priority = [Link](); and it is displayed. Then the
thread is started with the statement [Link]();. The thread is also made to sleep for 1000
milliseconds with the statement [Link](1000);. The information about whether the thread
is alive or not, is then retrieved and it is also displayed.

Choosing the approach of creating Threads

As we have seen, we can create children threads either by extending the Thread class or by
implementing the Runnable interface. In the first case, we extend the Thread class. Here we
have the provision, if need be, for overriding the other methods of the Thread class also and
thereby we can alter the behavior and have control over the children threads. This is along with
redefining the run() method of the Thread class. But in the case of creating the children threads
by implementing the Runnable interface, we are just confined to execute the contract given by
the interface and normally, we simply implement the run() method of the Runnable interface
to realize the function of the children threads. The difference cited here drives the programmers to
choose the appropriate method of creating the children threads. However, any of the two
approaches can be chosen in the normal circumstances.
14.7 Creating Multiple Children Threads

We know that when a java program is launched for execution, the main thread of the
program starts executing. We can even create our own threads within the scope of the
main thread, if need be. The following program illustrates creation of two children
threads, each accomplishing a job, by extending the Thread class.

Program-14.5 To illustrate creation of multiple threads

class ThreadA extends Thread


{
public void run()
{
int f = 1;
for(int i = 1; i <= 10; i++)
{
f *= i;
[Link]("factorial of " + i + " = " +
f);
}

[Link]("ThreadA exiting");
}
}

class ThreadB extends Thread


{
public void run()
{
int s = 0;
for(int i = 1; i <= 10; i++)
{
s += i;
[Link]("Sum of natural Numbers upto " +
i + "= " + s);
}
[Link]("ThreadB exiting");
}
}

class ThreadDemo2
{
public static void main(String s[])
{
ThreadA a = new ThreadA();
ThreadB b = new ThreadB();

[Link]();
[Link]();
}

[Link]("The Main thread exiting");

Input-Output

factorial of 1 = 1
Sum of natural Numbers upto 1= 1
factorial of 2 = 2
Sum of natural Numbers upto 2= 3
factorial of 3 = 6
Sum of natural Numbers upto 3= 6
factorial of 4 = 24
Sum of natural Numbers upto 4= 10
factorial of 5 = 120
Sum of natural Numbers upto 5= 15
factorial of 6 = 720
Sum of natural Numbers upto 6= 21
factorial of 7 = 5040
Sum of natural Numbers upto 7= 28
factorial of 8 = 40320
Sum of natural Numbers upto 8= 36
factorial of 9 = 362880
Sum of natural Numbers upto 9= 45
factorial of 10 = 3628800
Sum of natural Numbers upto 10= 55
ThreadA exiting
ThreadB exiting

The Main thread exiting

The class ThreadA extends the Thread class. The body of the run() of the class contains
code to find out and display the factorial of each number in the range 1 to 10. The class
ThreadB also extends the Thread class. The body of the run() of the class contains code to
find out and display the sum of the natural numbers up to each number in the range 1 to 10.
In the main() of the ThreadDemo2 class, an object of ThreadA class is created with
its reference in the variable a of ThreadA type with the statement ThreadA a = new
ThreadA();. An object of ThreadB class is also created with its reference in the variable b
of ThreadB type with the statement ThreadB b = new ThreadB();. The statements
[Link](); and [Link](); make the corresponding threads run. As a result of the
execution of the threads a and b concurrently, the factorials of each number from 1 to 10 and
the sum of the natural numbers up to each number in the range from 1 to 10 are computed and
they are displayed. Once the threads a and b finish their execution, the main thread exits.

The following program illustrates creation of two children threads, each accomplishing a
job, by implementing the Runnable interface.

Program-14.6 To illustrate creation of multiple threads

class ThreadA implements Runnable


{
public void run()
{
int f = 1;
for(int i = 1; i <= 10; i++)
{
f *= i;
[Link]("factorial of " + i + " = " +
f);
}
[Link]("ThreadA exiting");
}
}

class ThreadB implements Runnable


{
public void run()
{
int s = 0;
for(int i = 1; i <= 10; i++)
{
s += i;
[Link]("Sum of natural Numbers upto " +
i + " = " + s);
}
[Link]("ThreadB exiting");
}
}

class ThreadDemo4
{
public static void main(String s[])
{
ThreadA a = new ThreadA();
Thread t = new Thread(a);
[Link]();
ThreadB b = new ThreadB();
t = new Thread(b);
[Link]();

[Link](“The Main thread exiting”);


}
}

Input-Output

factorial of 1 = 1
factorial of 2 = 2
factorial of 3 = 6
factorial of 4 = 24
factorial of 5 = 120
factorial of 6 = 720
factorial of 7 = 5040
factorial of 8 = 40320
factorial of 9 = 362880
factorial of 10 = 3628800
ThreadA exiting
Sum of natural Numbers upto 1 = 1
Sum of natural Numbers upto 2 = 3
Sum of natural Numbers upto 3 = 6
Sum of natural Numbers upto 4 = 10
Sum of natural Numbers upto 5 = 15
Sum of natural Numbers upto 6 = 21
Sum of natural Numbers upto 7 = 28
Sum of natural Numbers upto 8 = 36
Sum of natural Numbers upto 9 = 45
Sum of natural Numbers upto 10 = 55
ThreadB exiting

The Main thread exiting

The class ThreadA implements the Runnable interface. The implementation of the run() of
the Runnable interface in the ThreadA class contains code to find out and display the factorial
of each number in the range 1 to 10. The class ThreadB also implements the Runnable
interface. The implementation of the run() of the interface in the ThreadB class contains
code to find out and display the sum of the natural numbers up to each number in the range 1 to
10.
In the main() of the ThreadDemo4 class, an object of ThreadA class is created with
its reference in the variable a of ThreadA type with the statement ThreadA a = new
ThreadA();. An object of Thread class is created through the object a of ThreadA type
with its reference in the variable t of Thread type with the statement Thread t = new
Thread(a);. The statement [Link](); makes the thread a start running.
An object of ThreadB class is also created with its reference in the variable b of
ThreadB type with the statement ThreadB b = new ThreadB();.An object of Thread
class is created through the object b of ThreadB type with its reference in the variable t of
Thread type with the statement t = new Thread(b); The statement [Link]();
makes the thread b also start running.
As a result of the execution of the threads a and b concurrently, the factorials of each
number from 1 to 10 and the sum of the natural numbers up to each number in the range from 1
to 10 are computed and they are displayed. Once the threads a and b finish their execution, the
main thread exits.

14.8 Forming Groups of Threads

Sometimes we may need to split a specific job across multiple threads. Once they start executing,
under some unavoidable circumstances, we may be forced to suspend all the threads for some
time and resume all the threads at a later point of time. The need for controlling multiple threads
has necessitated grouping of threads. Fortunately Java provides a built-in class by name
ThreadGroup for this purpose. Grouping of threads through the ThreadGroup class objects
offers us a convenient way to manage groups of threads.

The ThreadGroup class is used to create groups of threads. It has the following two
constructors

1. ThreadGroup(String name)
2. ThreadGroup(ThreadGroup parentThread, String name)

The constructor ThreadGroup(String groupName) creates a new thread group with the
current thread as the parent thread and the name of the group being groupName.

The constructor ThreadGroup(ThreadGroup parentThread, String name)


creates a new group with the argument parentThread as the parent thread and the name of the
group being groupName.

Following are the methods of the ThreadGroup class and their descriptions, which are used to
perform different operations over thread groups.

Method Description
int activeCount() The activeCount() method returns the
number of threads in the group and any other
groups for which the invoking thread is a
parent.
int activeGroupCount() The activeGroupCount() method returns
the number of groups for which the invoking
thread is a parent.

final void destroy() The destroy() method destroys the thread


group on which it is called

int enumerate(Thread group[] The enumerate() method collects the threads in


the thread group into the array group[]

void list() The list() method lists the information


about the group

String toString() The toString() returns the string


equivalent of the group

final String getName() The getName() method returns the name of


a thread in the group

final Boolean interrupt() The interrupt() method invokes the


interrupt() method of all the threads in the
group

final Boolean isDaemon() The isDaemon() method returns true if


the group is a daemon group. Otherwise, it
returns false.

The following program illustrates the concept of the thread groups.

Program-14.7 To illustrate thread groups

class ThreadA extends Thread


{
ThreadA(ThreadGroup tg, String tn)
{
super(tg, tn);
start();
}
public void run()
{
try
{
sleep(1000);
}
catch(Exception e)
{
}
}

class ThreadGroupDemo
{
public static void main(String s[])
{
ThreadGroup g1 = new ThreadGroup("g1");

ThreadA a1 = new ThreadA(g1, "a1");


ThreadA a2 = new ThreadA(g1, "a2");

ThreadGroup g2 = new ThreadGroup("g2");

ThreadA a3 = new ThreadA(g1, "a3");


ThreadA a4 = new ThreadA(g1, "a4");

ThreadA a[] = new ThreadA[2];


[Link](a);
[Link]("Threads in the group g1");
for(int i = 0; i < [Link]; i++)
[Link](a[i].getName());

[Link](a);
[Link]("Threads in the group g2");
for(int i = 0; i < [Link]; i++)
[Link](a[i].getName());

}
}

Input-Output:

Threads in the group g1


a1
a2
Threads in the group g2
a3
a4
Two thread groups are created with the names g1 and g2 with the statements ThreadGroup
g1 = new ThreadGroup("g1"); and ThreadGroup g2 = new
ThreadGroup("g2");. Two threads a1 and a2 of ThreadA class are made to belong to the
thread group g1 with the statements
ThreadA a1 = new ThreadA(g1, "a1");
ThreadA a2 = new ThreadA(g1, "a2");

And the other two more threads a3 and a4 of ThreadA class are made to belong to the thread
group g2 with the statements

ThreadA a3 = new ThreadA(g1, "a3");


ThreadA a4 = new ThreadA(g1, "a4");

The threads in the group g1 are collected into the array a of type ThreadA with the statement
[Link](a);. The getName() method of the ThreadGroup is then used to get the
name of each thread in the group g1. Similarly, The threads in the group g2 are collected into the
array a of type ThreadA with the statement [Link](a);. The getName()
method of the ThreadGroup is then used to get the name of each thread in the group g2 also.
The names of the threads in both the groups are displayed.

14.9 Thread Priorities

In a multithreaded program, it is natural to assign different priorities to the threads so that the
some threads are given preference over the others depending on the requirement. In java, it is
possible to assign priority levels to threads. Higher the priority to a thread, higher is the chance
for the thread to get the attention of CPU. The Thread class has three constants
MAX_PRIORITY, MIN_PRIORITY and NORM_PRIORITY defined in it. The maximum
priority level value is 10 and that of the minimum priority level value is 1. The normal priority
level value is 5. These constants are public, static and final members of the class. We
can also set the priority level for a thread between the minimum level and the maximum levels
with the help of setPriority() method of the Thread class.

The prototype of the method is as follows:

public final void setPriority(int priority_level)


We can also retrieve the priority level of a thread with the use of another method
getPriority() of the Thread class. The prototype of the method is as follows:

public final int getPriority()

The following program illustrates thread priorities. Here three threads a, b and c of type
ThreadA, ThreadB and ThreadC are given minimum priority, maximum priority and normal
priority levels respectively and they are started. The output of the program shows how the
attention of CPU is switched over from one thread to another because of the priorities assigned.
Note that the thread b finishes first since it has the highest priority. The thread a with the
minimum priority finishes before the thread c as it was started before the thread c was started.

Program-14.8 To illustrate thread priorities

class ThreadA extends Thread


{
public void run()
{

for(int i = 1; i <= 5; i++)

[Link]("Executing ThreadA ");

[Link]("ThreadA Quitting");
}
}

class ThreadB extends Thread


{
public void run()
{

for(int i = 1; i <= 5; i++)

[Link]("Executing threadB ");

[Link]("ThreadB Quitting");
}
}

class ThreadC extends Thread


{
public void run()
{

for(int i = 1; i <= 5; i++)

[Link]("Executing threadC");

[Link]("ThreadC Quitting");
}
}

class ThreadPriority
{
public static void main(String s[])
{
ThreadA a = new ThreadA();
ThreadB b = new ThreadB();
ThreadC c = new ThreadC();

[Link](Thread.MIN_PRIORITY);
[Link](Thread.MAX_PRIORITY);
[Link](5);

[Link]("ThreadA started");
[Link]();
[Link]("ThreadB started");
[Link]();
[Link]("ThreadC started");
[Link]();
}

Input-Output
ThreadA started
ThreadB started
Executing ThreadA
Executing ThreadA
Executing threadB
Executing threadB
Executing threadB
Executing threadB
Executing threadB
ThreadB Quitting
ThreadC started
Executing ThreadA
Executing ThreadA
Executing ThreadA
ThreadA Quitting
Executing threadC
Executing threadC
Executing threadC
Executing threadC
Executing threadC
ThreadC Quitting

14.10 Synchronization

We know that multithreading enables us to increase the speed of execution of a program and to
optimize the computer resources usage. Normally, the multiple threads of a single program run
asynchronously. This is acceptable if the threads do not share any common resource. Under some
circumstances, more than one thread in a program may need to share the same resource. In this
case synchronicity of the threads involved should be ensured. In other words, we need to ensure
that any point of time, only one thread has access to the resource so that the integrity of the
resource is preserved. The process by which this is achieved is called synchronization. Java
provides a language level support itself for achieving synchronization. (The languages like C and
C++ have to rely on the underlying operating system for the purpose). For now, we will delve
into a program which has unsynchronized threads and understand the limitation of them so that
we can feel the need for synchronization mechanism and appreciate it.

Program-14.9 To illustrate Unsynchronized threads

class Counter
{
int i;

Counter(int j)
{
i = j;
}

void incr()
{
i++;
}

void display()
{
[Link](" i = " + i);
}
}

class CounterIncr extends Thread


{

Counter c;

CounterIncr(Counter c1)
{
c = c1;
}

public void run()


{
for(int i = 1; i <= 100000; i++)
[Link]();

}
}

class UnSynchDemo
{
public static void main(String s[])
{
Counter c = new Counter(0);

CounterIncr ci[] = new CounterIncr[10];

for(int i = 0; i < 10; i++)


{
ci[i] = new CounterIncr(c);
ci[i].start();
}

for(int i = 0; i < 10; i++)


{
try
{
ci[i].join();
}

catch(Exception e)
{}
[Link]();
}

}
}
Input-Output:

i = 453995
i = 529526
i = 555114
i = 581208
i = 607197
i = 732335
i = 745303
i = 771886
i = 797961
i = 832335

The class Counter is defined with a member data i of int type. It also has a constructor and
two member methods namely incr() and display() which are to increment the value of
the member data and to display the member data value of an object of the class respectively.
The class ConuterIncr extends the class Thread and thus each object of the class
CounterIncr represents a thread of control in the program. The class has an object c of type
Counter as its member. It also has a constructor to create its objects and the definition for the
run() method. In the body of the run() method, the method incr() of the class Counter
is invoked 100000 times as the for loop variable i takes on the values from 1 to 100000.
In the main() method of the UnSynchDemo class, an object of Counter class type is
created with its reference in the variable c with the statement Counter c = new
Counter(0);. Note that the initial value of the member data i of the object c is 0. An array of
10 references of type CounterIncr is also created with the statement
CounterIncr ci[] = new CounterIncr[10];. Each of the references in the
array is assigned a reference to an object of CounterIncr with c (object of Counter type) as
the argument to the invoking constructor and all the 10 threads are then started for execution with
the statements

for(int i = 0; i < 10; i++)


{
ci[i] = new CounterIncr(c);
ci[i].start();
}

Note that the same object c is the target for all the 10 threads.

The main thread is made to wait for each of the 10 threads to complete by invoking the
join() method with each thread object and the value of the member data of the object c of
Counter type is displayed. This is accomplished by the following segment of code

for(int i = 0; i < 10; i++)


{
try
{
ci[i].join();
}
catch(Exception e)
{}
[Link]();
}

The final value of the member data i of Counter class should be 1,00,0000 as each of the 10
threads tries to increment the value by one 1,00,000 times. But as the threads increment the
member data i of Counter class asynchronously, the required number of increments is not
reflected in the final value. Here all the 10 threads are in their race to complete their task and they
eventually corrupt the value of the member data I of the Counter class object c. this results in the
incorrect value of the member data [Link] condition is called race condition.

We can eliminate the above problem by enforcing synchronization among threads. Central to
achieving synchronization is the concept of the monitor. The monitor is also called the
semaphore. At any given point of time, only one thread can own the monitor. The monitor here is
used as a mutually exclusive lock. All other threads which are in need of the monitor have to wait
for the currently holding thread to release the monitor. We can achieve synchronization in two
ways. They are:
1) The synchronized method and
2) The synchronized statement.
We will discuss these in detail here.

1) Synchronized Method

We can use the keyword synchronized keyword in the header of a method to make it a
synchronized method. Once a method is made synchronized, if one thread invokes the method, it
gets a lock on the method and all the other remaining threads have to wait for the current thread
to release the lock. This ensures the synchronicity among all the threads.

The genral form of making a method synchronized is as follows:

synchronized return-type method_name(arguments)


{
//body
}

The problem in the previous program can be eliminated by making the member method incr()
of the Counter class synchronized. The program is reproduced below after incorporating the
synchronized feature.
Program-14.10 To illustrate Synchronized threads

class Counter
{
int i;

Counter(int j)
{
i = j;
}

synchronized void incr()


{
i++;
}

void display()
{
[Link](" i = " + i);
}
}

class CounterIncr extends Thread


{

Counter c;

CounterIncr(Counter c)
{
this.c = c;
}

public void run()


{
for(int i = 1; i <= 100000; i++)
[Link]();

class SynchDemo
{
public static void main(String s[])
{
Counter c = new Counter(0);

CounterIncr ci[] = new CounterIncr[10];


for(int i = 0; i < 10; i++)
{
ci[i] = new CounterIncr(c);
ci[i].start();

for(int i = 0; i < 10; i++)


{
try{
ci[i].join();}
catch(Exception e)
{}
[Link]();

}
}

Input-Output:

i = 764367
i = 927120
i = 998134
i = 1000000
i = 1000000
i = 1000000
i = 1000000
i = 1000000
i = 1000000
i = 1000000

The class Counter is defined with a member data i of int type. It also has a constructor and
two member methods namely incr() and display() which are to increment the value of
the member data and to display the member data value of an object of the class respectively.
Note that the incr() method is synchronized with the use of the keyword synchronized in its
header. The class ConuterIncr extends the class Thread and thus each object of the class
CounterIncr represents a thread of control in the program. The class has an object c of type
Counter as its member. It also has a constructor to create its objects and the definition for the
run() method. In the body of the run() method, the method incr() of the class Counter
is invoked 100000 times as the for loop variable i takes on the values from 1 to 100000.
In the main() method of the SynchDemo class, an object of Counter class type is
created with its reference in the variable c with the statement Counter c = new
Counter(0);. Note that the initial value of the member data i of the object c is 0. An array of
10 references of type CounterIncr is also created with the statement
CounterIncr ci[] = new CounterIncr[10];. Each of the references in the
array is assigned a reference to an object of CounterIncr with c (object of Counter type) as
the argument to the invoking constructor and all the 10 threads are then started for execution with
the statements

for(int i = 0; i < 10; i++)


{
ci[i] = new CounterIncr(c);
ci[i].start();
}

Note that the same object c is the target for all the 10 threads.

The main thread is made to wait for each of the 10 threads to complete by invoking the join()
method with each thread object and the value of the member data of the object c of Counter
type is displayed. This is accomplished by the following segment of code

for(int i = 0; i < 10; i++)


{
try
{
ci[i].join();
}

catch(Exception e)
{}
[Link]();
}

The final value of the member data i of Counter class has become 1,00,0000 as each of the 10
threads has incremented the value by one 1,00,000 times. This has neen possible as the threads
increment the member data i of Counter class synchronously.

Let us now discuss achieving synchronization through the use of the synchronized statement.

2) The synchronized statement.

The synchronized statement is another way of achieving synchronization. This becomes handy
when we need to synchronize access to the objects which are not designed to have synchronized
methods. The general form of the usage of the synchronized statements is as follows:

synchronized(object)
{
statements // method calls
}
Where object is an object of a class which does not have synchronized methods and but we need
to achieve synchronization on the object. The body of the synchronized block contains method
calls with the object. Once a thread gets into the synchronized block, it acquires a lock on the
object and other threads have to wait till the current thread releases the lock. The following
program illustrates the usage of synchronized statement to achieve synchronization.

Program-14.11 To illustrate Synchronized threads

class Counter
{
int i;

Counter(int j)
{
i = j;
}

void incr()
{
i++;
}

void display()
{
[Link](" i = " + i);
}
}

class CounterIncr extends Thread


{

Counter c;

CounterIncr(Counter c1)
{
c = c1;
}

public void run()


{
for(int i = 1; i <= 100000; i++)
synchronized(c)
{
[Link]();
}

}
}

class SynchDemo1
{
public static void main(String s[])
{
Counter c = new Counter(0);

CounterIncr ci[] = new CounterIncr[10];


for(int i = 0; i < 10; i++)
{
ci[i] = new CounterIncr(c);
ci[i].start();
}

for(int i = 0; i < 10; i++)


{
try{
ci[i].join();}
catch(Exception e)
{}
[Link]();

}
}

Input-Output:

i = 1000000
i = 1000000
i = 1000000
i = 1000000
i = 1000000
i = 1000000
i = 1000000
i = 1000000
i = 1000000
i = 1000000

The class Counter is defined with a member data i of int type. It also has a constructor and
two member methods namely incr() and display() which are to increment the value of
the member data and to display the member data value of an object of the class respectively.
The class Counter does not have synchronized methods.
The class ConuterIncr extends the class Thread and thus each object of the class
CounterIncr represents a thread of control in the program. The class has an object c of type
Counter as its member. It also has a constructor to create its objects and the definition for the
run() method. In the body of the run() method, the method incr() of the class Counter
is invoked 100000 times as the for loop variable i takes on the values from 1 to 100000.
Note that the method incr() is invoked within the synchronized block

synchronized( c )
{
[Link]();
}

In the main() method of the SynchDemo1 class, an object of Counter class type is created
with its reference in the variable c with the statement Counter c = new
Counter(0);. Note that the initial value of the member data i of the object c is 0. An array
of 10 references of type CounterIncr is also created with the statement
CounterIncr ci[] = new CounterIncr[10];. Each of the references in
the array is assigned a reference to an object of CounterIncr with c (object of Counter
type) as the argument to the invoking constructor and all the 10 threads are then started for
execution with the statements

for(int i = 0; i < 10; i++)


{
ci[i] = new CounterIncr(c);
ci[i].start();
}

Note that the same object c is the target for all the 10 threads.

The main thread is made to wait for each of the 10 threads to complete by invoking the join()
method with each thread object and the value of the member data of the object c of Counter
type is displayed. This is accomplished by the following segment of code

for(int i = 0; i < 10; i++)


{
try
{
ci[i].join();
}

catch(Exception e)
{}
[Link]();
}

The final value of the member data i of Counter class has become 1,00,0000 as each of the 10
threads has incremented the value by one 1,00,000 times. This has been possible as the threads
increment the member data i of Counter class synchronously because of the usage of
synchronized statement.

14.11 Deadlock

We have learnt that synchronized methods and synchronized statements are useful to ensure that
multiple threads execute in a synchronized fashion. However, the locking mechanism employed
by the synchronization process leads to a problem situation called deadlock at times. The
deadlock situation arises when two threads wait indefinitely for each other to release locks on the
objects they have control on. Suppose the thread A has access to the method f() and waits for
another method g(), and the thread B has a lock on the method g() and waits for the method
f(). Here both the threads A and B wait indefinitely to continue further. Hence the threads A and
B are said to be in a deadlock condition. Here we have no other option but to terminate them
abruptly and resign the program avoiding the deadlock situation. The following program
illustrates a deadlock situation.

Program-14.12 To illustrate deadlock situation

class A
{
B b;

synchronized void a1()


{
[Link]("Inside the method a1 of the
class A");
b.b2();

synchronized void a2()


{
[Link]("Inside the method a2 of the
class A");

class B
{

A a;

synchronized void b1()


{
[Link]("Inside the method b1 of the
class B");
a.a2();

synchronized void b2()


{
[Link]("Inside the method b2 of the
class B");

class ThreadA extends Thread


{
A a;

ThreadA(A a)
{
this.a = a;
}

public void run()


{
for(int i = 1; i <= 10; i++)
a.a1();
}
}

class ThreadB extends Thread


{

B b;

ThreadB(B b)
{
this.b = b;
}

public void run()


{
for(int i = 1; i <= 10; i++)
b.b1();

}
}

class Deadlock
{
public static void main(String s[])
{
A a = new A();
B b = new B();

a.b = b;
b.a = a;

ThreadA ta = new ThreadA(a);


ThreadB tb = new ThreadB(b);

[Link]();
[Link]();
}

Input-Output:

Inside the method a1 of the class A


Inside the method b1 of the class B
^c

The class A is defined with an object of B class type as its member data. It also has two
synchronized member methods namely a1() and a2().The class B is defined with an object of
A class type as its member data. It also has two synchronized member methods namely b1() and
b2(). The class ThreadA extends the Thread class and thus an object of ThreadA class
represents a thread of control. The ThreadA class contains an object of A class type as its
member data and a constructor. It also has the definition for the run() method. In the body of
the run(), the method a1 of A class is invoked 10 times. The class ThreadB extends the
Thread class and thus an object of ThreadA class represents a thread of control. The
ThreadB class contains an object of B class type as its member data and a constructor. It also
has the definition for the run() method. In the body of the run(), the method b1 of B class is
invoked 10 times.

In the main() method of the Deadlock class, objects of classes A and B are created
with their references in a and b respectively. Since the class A contains an object of B type as its
member data and the class B contains an object of A type as its member data, the statements a.b
= b; and b.a = a; set the values of the member data of each of the classes. An object of the
class ThreadA is created with its reference in the variable ta with the statement Thread Ata
= new ThreadA(a); An object of the class ThreadB is created with its reference in the
variable tb with the statement ThreadB tb = new ThreadB(b); The two threads are
then started for execution. The thread ta begins to execute and invokes the synchronized method
a1 of the class A through the object a. Thus the object a is locked by the thread ta. The thread
tb also begins to execute and invokes the synchronized method b1 of the class B through the
object b. Thus the thread tb has a lock on the object b. The thread ta, to continue further needs
a lock on b since it has to invoke the synchronized method b2() of the object b. but it is held by
the thread tb. The thread tb, to continue further needs a lock on the object a since it has to
invoke the synchronized method a2() of the object a. So both the threads ta and tb wait
indefinitely for each other to release the locks on their required objects, which does not end
fruitfully. We have to thus terminate the program abruptly by pressing ctrl+c.

14.12 Suspending and Resuming Threads

Under some circumstances we may need to pause a thread for some time and resume the thread at
a later point of time. Before java 2, there were two methods for the purpose namely suspend()
and resume() in the Thread class. These two methods were deprecated in Java 2 since they
had the potential to create deadlocks. Java has provided alternative means for suspending and
resuming threads safely. It is with the help of the methods notify(), wait() and
notifyall(), which belong to the Object class. A boolean variable, as it takes a value
which can be either true or false, is used here to toggle between wait() and notify() or
notifyAll() method calls. As we only take the responsibility of setting or unsetting the value
of the boolean variable to toggle between waiting state and ready state, the control rests with us
and thus enables us to avoid deadlock situation.

The wait() method

The wait() method when invoked in a synchronized method makes the thread release the
object lock and also makes the thread wait for sometime until it is told to resume.
The syntax of its usage is as follows

synchronized type method_name(arguments)


{
statements;
wait();
}

The notify() method


The notify() method when invoked inside a synchronized method makes waiting thread
resume its execution.

synchronized type method_name(arguments)


{
statements;

notify();
}

The notifyAll() method

The notifyAll() method when invoked inside a synchronized method makes all the waiting
threads resume their operation. This is used when all the waiting threads can resume their
execution without any hurdles in getting their required resources.
The syntax of its usage is as follows:

synchronized type method_name(arguments)


{
statements;

notifyAll();
}

Let us now see how threads are suspended and are resumed with the help of the above methods.
Here we have two threads of type ThreadA and ThreadB. Both are suspended for sometime
and resumed later. The output of the program shows the effect of suspending and later resuming
them.

Program-14.13 To illustrate suspending and resuming threads

class ThreadA extends Thread


{
boolean flag = false;
public void run()
{
try
{
for(int i = 1; i <= 10; i++)
{

[Link]("ThreadA i = " + i);


sleep(1000);
synchronized(this)
{

while (flag)
[Link]();

}
}

}
catch(Exception e)
{
}

void Suspend()
{
flag = true;
}

synchronized void Resume()


{
flag = false;
notify();
}

class ThreadB extends Thread


{
boolean flag = false;

public void run()


{
try
{
for(int j = 1; j <= 10; j++)
{

[Link]("ThreadB j = " + j);


sleep(1000);

synchronized(this)
{

while (flag)
[Link]();
}
}

}
catch(InterruptedException e)
{ }
}

void Suspend()
{
flag = true;
}

synchronized void Resume()


{
flag = false;
notify();
}

class Sus_Res
{
public static void main(String s[])
{
ThreadA a = new ThreadA();
ThreadB b = new ThreadB();

[Link]();
[Link]();

try{
[Link](2000);

[Link]();
[Link]("ThreadA suspended");
[Link](2000);

[Link]();
[Link]("ThreadA resumed");
[Link](2000);

[Link]();
[Link]("ThreadB suspended");
[Link](2000);

[Link]();
[Link]("ThreadB resumed");
[Link](2000);
}
catch(InterruptedException ie)
{
}

try{
[Link]();
[Link]();
}
catch(Exception e){}
}
}

Input-Output
ThreadA i = 1
ThreadB j = 1
ThreadA i = 2
ThreadB j = 2
ThreadA suspended
ThreadB j = 3
ThreadB j = 4
ThreadA resumed
ThreadA i = 3
ThreadB j = 5
ThreadA i = 4
ThreadB j = 6
ThreadB suspended
ThreadA i = 5
ThreadA i = 6
ThreadB resumed
ThreadB j = 7
ThreadA i = 7
ThreadB j = 8
ThreadA i = 8
ThreadB j = 9
ThreadA i = 9
ThreadB j = 10
ThreadA i = 10

The class ThreadA extends the Thread class and hence an instance of the ThreadA class
represents a thread of control. It has a boolean variable namely, flag and it is initialized with
the false value. Note here that the value of the boolean variable flag determines whether
the thread is to be suspended or to be resumed. The run() method of the class ThreadA has a
loop to iterate from 1 to 10 and in each iteration, the value of the loop variable is displayed. The
body of the loop also has a synchronized block, which when entered into, invokes the method
wait() as long as the flag value is true. The member method suspend()of the
ThreadA class is defined to set the value true to the variable flag. The ThreadA class
has another member method resume(), which sets the value false to the flag variable and
also invokes the method notify(). So here the user-defined member methods suspend()
and resume() play the role of toggling the value of the flag variable, which in turn is
responsible for making a thread of ThreadA type wait for some time (through wait()) and
resume (through notify()).
The class ThreadB also extends the Thread class and hence an instance of the
ThreadB class represents a thread of control. It has a boolean variable namely, flag and it is
initialized with the false value. Note here that the value of the boolean variable flag
determines whether the thread is to be suspended or to be resumed. The run() method of the
class ThreadB has a loop to iterate from 1 to 10 and in each iteration, the value of the loop
variable is displayed. The body of the loop also has a synchronized block, which when entered
into, invokes the method wait() as long as the flag value is true. The member method
suspend()of the ThreadB class is defined to set the value true to the variable
flag. The ThreadA class has another member method resume(), which sets the value
false to the flag variable and also invokes the method notify(). So here the user-defined
member methods suspend() and resume() play the role of toggling the value of the flag
variable, which in turn is responsible for making a thread of ThreadA type wait for some time
(through wait()) and resume (through notify()).
In the main() method of the Sus_Res class, an object of ThreadA class is created
with its reference in the variable ta with the statement ThreadA ta = new ThreadA();
and an object of ThreadB class also is created with its reference in the variable tb with the
statement ThreadB tb = new ThreadB();. The two threads are started for execution.
Bothe the threads are suspended and resumed by invoking the member methods suspend() and
resume() of their respective classes with insertion of time delays of 2000 milliseconds in
between just to understand the execution pattern of the threads. We can see that both the threads
get executed alternatively in accordance with the order their suspension and resumption.

Following is another program for illustrating suspending and resuming threads with the use of
wait(), notify() methods of the Object class. Here also we have two classes namely,
ThreadA and ThreadB. These are similar to the same named classes in the previous program
except the fact that the run() method of ThreadA class has the code segment for finding the
sum of natural numbers up to 10 and that of the class ThreadB has the code segment for finding
the factorial of numbers from 1 to 10. The rest remain the same.

Program-14.15 To illustrate suspending and resuming threads

class ThreadA extends Thread


{
boolean flag = false;
public void run()
{
int s = 0;
try
{
for(int i = 1; i <= 10; i++)
{
s += i;

[Link]("(ThreadA) Sum upto " + i


+ " = " + s);
sleep(1000);
synchronized(this)
{

while (flag)
[Link]();

}
}

}
catch(Exception e)
{
}

void Suspend()
{
flag = true;
}

synchronized void Resume()


{
flag = false;
notify();
}

class ThreadB extends Thread


{
boolean flag = false;

public void run()


{
int f = 1;
try
{
for(int j = 1; j <= 10; j++)
{
f *= j;

[Link]("(ThreadB) factorial of "


+ j + " = " + f);
sleep(1000);

synchronized(this)
{

while (flag)
[Link]();
}
}

}
catch(InterruptedException e)
{ }
}

void Suspend()
{
flag = true;
}

synchronized void Resume()


{
flag = false;
notify();
}

class Sus_Res1
{
public static void main(String s[])
{
ThreadA a = new ThreadA();
ThreadB b = new ThreadB();

[Link]();
[Link]();

try{
[Link](2000);
[Link]();
[Link]("ThreadA suspended");
[Link](2000);

[Link]();
[Link]("ThreadA resumed");
[Link](2000);

[Link]();
[Link]("ThreadB suspended");
[Link](2000);

[Link]();
[Link]("ThreadB resumed");
[Link](2000);
}
catch(InterruptedException ie)
{
}

try{
[Link]();
[Link]();
}
catch(Exception e){}
}
}

Input-Output

(ThreadA) Sum upto 1 = 1


(ThreadB) factorial of 1 = 1
(ThreadA) Sum upto 2 = 3
(ThreadB) factorial of 2 = 2
ThreadA suspended
(ThreadB) factorial of 3 = 6
(ThreadB) factorial of 4 = 24
ThreadA resumed
(ThreadA) Sum upto 3 = 6
(ThreadB) factorial of 5 = 120
(ThreadA) Sum upto 4 = 10
(ThreadB) factorial of 6 = 720
ThreadB suspended
(ThreadA) Sum upto 5 = 15
(ThreadA) Sum upto 6 = 21
ThreadB resumed
(ThreadB) factorial of 7 = 5040
(ThreadA) Sum upto 7 = 28
(ThreadB) factorial of 8 = 40320
(ThreadA) Sum upto 8 = 36
(ThreadB) factorial of 9 = 362880
(ThreadA) Sum upto 9 = 45
(ThreadB) factorial of 10 = 3628800
(ThreadA) Sum upto 10 = 55

As can be observed from the output, Both the threads of type ThreadA and ThreadB type
continue to execute alternatively as we have suspended and resumed them in the code with
insertion of time delays between the method calls.

14.13 Producer-Consumer Relationship between Threads

In a multithreaded program, a situation may arise wherein one thread produces some data and
another thread consumes the data. The relationship between the threads involved in the
transaction is rightly called Producer-Consumer problem. The most important facts to be taken
care of while implementing producer-consumer relationship between two threads are 1) The
producer should produce data before the consumer consumes it 2) The producer should produce
the next data only when the previously produced data has already been consumed. The following
program tries to implement producer-consumer relationship between two threads. Let us first
understand the limitation encountered by the program in implementing the relationship without
the use of wait() and notify() methods. . We will see how this problem is circumvented
with the use of the methods in the next program later.

Program-14.16 To illustrate Producer-Consumer Relationship between threads

class DataStock
{
int n;

synchronized void add(int n)


{
this.n = n;
[Link]("Data Produced" + n);
}

synchronized void remove()


{
[Link]("Data Consumed" + n);
}
}

class Producer implements Runnable


{
DataStock ds;
Producer(DataStock ds)
{
[Link] = ds;

new Thread(this, "Producer").start();


}

public void run()


{
int i = 1;

while (true)
{
[Link](i++);
}
}

class Consumer implements Runnable


{
DataStock ds;

Consumer(DataStock ds)
{
[Link] = ds;

new Thread(this, "Consumer").start();


}

public void run()


{

while (true)
{
[Link]();
}
}

class PCError
{
public static void main(String s[])
{
DataStock ds = new DataStock();

Producer p = new Producer(ds);


Consumer c = new Consumer(ds);
}
}
Input-Output:

Data Produced1
Data Consumed1
Data Consumed1
Data Produced2
Data Consumed2
Data Produced3
Data Produced4
Data Consumed4
Data Produced5
Data Consumed5
Data Consumed5
Data Produced6
Data Consumed6
Data Produced7
Data Consumed7
Data Produced8

The class DataStock is defined with a member data n of int type and it also has two
synchronized member methods add() and remove(). The purpose of the add() method
is to assign a value to the int variable n and display a message on the screen that the method
add() has produced the data value, and that of the remove() is to read the value of the
variable and to display it on the screen. The class Producer implements the Runnable
interface and thus an object of the class can be used to create a thread of Producer type. The
class has a reference ds of type DataStock. The constructor of the class sets the member data
ds with an argument of the same type and a new thread of type Producer type is started with
the statement new Thread(this, "Producer").start();. The body of the run()
method has a variable of int type i with its initial value [Link] also has a while loop within
which the add() method of the DataStock class is called repeatedly with the value of the
int variable i as an argument to it. Note that in each call to the add(), the value of the
variable i is incremented

The class Consumer implements the Runnable interface and thus an object of the class can be
used to create a thread of Consumer type. The class Consumer also has a reference ds of type
DataStock. The constructor of the class sets the member data ds with an argument of the
same type and a new thread of type Consumer type is started with the statement new
Thread(this, "Consumer").start();. The run() method of the class has a while
loop within which the remove() method of the DataStock class is called repeatedly.
In the main() of the class PCError, an object of the class DataStock is created
with its reference in the variable ds with the statement DataStock ds = new
DataStock(); and Two objects, one each of the Producer class and the class Consumer
are also created with the object ds as an actual argument to their respective constructors with the
statements Producer p = new Producer(ds); and Consumer c = new
Consumer(ds);. Note that the threads of both Producer type and Consumer type are
automatically started on the creation of the objects of their type as the start() method is
invoked within the constructors themselves.
The output produced by the program on its execution shows that there is no co-ordination
between the producer and the consumer classes. We can see that the consumer has read the same
data value more than once. This is in spite of the fact that both the add() method and the
remove() methods are synchronized. Mere synchronization has not been able to achieve the
expected producer consumer relationship between the Producer and the Consumer objects.
The following program alleviates this problem by the use of the wait() and the notify()
methods.

Program-14.17 To illustrate Producer-Consumer Relationship between threads

class DataStock
{
int n;

boolean available = false;

synchronized void remove()


{

if(!available)
{
try
{
wait();
}
catch(InterruptedException ie)
{
[Link]("Interrupted
Exception caught");
}
}
[Link]("Data Consumed" + n);
[Link]();
available = false;
notify();

}
synchronized void add(int n)
{

if(available)
{
try
{
wait();
}
catch(InterruptedException ie)
{
[Link]("Interrupted
Exception caught");
}
}
this.n = n;
available = true;
[Link]("Data Produced" + n);
notify();
}

class Producer implements Runnable


{
DataStock ds;

Producer(DataStock ds)
{
[Link] = ds;

new Thread(this, "Producer").start();


}

public void run()


{
int i = 1;

while (true)
{
try
{
[Link](1000);
}
catch(Exception e)
{
}

[Link](i++);
}
}

class Consumer implements Runnable


{
DataStock ds;

Consumer(DataStock ds)
{
[Link] = ds;

new Thread(this, "Consumer").start();


}

public void run()


{

while (true)
{
try
{
[Link](1000);
}
catch(Exception e)
{
}

[Link]();
}
}

class PC
{
public static void main(String s[])
{
DataStock ds = new DataStock();

Producer p = new Producer(ds);


Consumer c = new Consumer(ds);
}
}

Input-Output:

Data Produced1
Data Consumed1
Data Produced2
Data Consumed2

Data Produced3
Data Consumed3

Data Produced4
Data Consumed4

Data Produced5
Data Consumed5

Data Produced6
Data Consumed6

^c

The class DataStock is defined with two member data n and available of type int and
boolean respectively. The Boolean variable available is initialized with false. It also has two
synchronized member methods add() and remove().
The purpose of the add() method is to assign a value to the int variable n and
display a message on the screen that the method add() has produced the data value. The value
of the boolean variable determines whether to continue assigning to the int variable n or not.
If the variable available has got the value true, it means here that the data value n has yet to
be read by the remove() method. As a result, the wait() method is invoked to wait for the
remove() method to read the data value. On the other hand, if the variable available has
got the value false, it means that the data value in n has been read by the remove() method
and therefore the add() method proceeds with assigning a value to the variable n and setting the
value true to the variable available. It also displays that the method has produced the data
value and invokes the notify() method. The notify() method on its execution notifies the
waiting thread to get the lock on the DataStock object.
The purpose of the remove() is to read the value of the variable n and to display it on
the screen. The value of the boolean variable determines whether to continue reading from the
int variable n or not. If the variable available has got the value false, it means here that
the data value n is not yet available. As a result, the wait() method is invoked to wait for the
add() method to assign the data value. On the other hand, if the variable available has got
the value true, it means that the data value in n is made available by the add() method and
therefore the remove() method proceeds with reading the value from the variable n and
setting the value false to the variable available. It also displays that the method has
consumed the data value and invokes the notify() method. The notify() method on its
execution notifies the waiting thread to get the lock on the DataStock object.
The class Producer implements the Runnable interface and thus an object of the
class can be used to create a thread of Producer type. The class has a reference ds of type
DataStock. The constructor of the class sets the member data ds with an argument of the same
type and a new thread of type Producer type is started with the statement new
Thread(this, "Producer").start();. The body of the run() method has a variable
of int type i with its initial value [Link] also has a while loop within which the add() method
of the DataStock class is called repeatedly with the value of the int variable i as an
argument to it. Note that in each call to the add(), the value of the variable i is incremented

The class Consumer implements the Runnable interface and thus an object of the class can be
used to create a thread of Consumer type. The class Consumer also has a reference ds of type
DataStock. The constructor of the class sets the member data ds with an argument of the
same type and a new thread of type Consumer type is started with the statement new
Thread(this, "Consumer").start();. The run() method of the class has a while
loop within which the remove() method of the DataStock class is called repeatedly.
In the main() of the class PC, an object of the class DataStock is created with its
reference in the variable ds with the statement DataStock ds = new DataStock();
and Two objects, one each of the Producer class and the class Consumer are also created
with the object ds as an actual argument to their respective constructors with the statements
Producer p = new Producer(ds); and Consumer c = new Consumer(ds);.
Note that the threads of both Producer type and Consumer type are automatically started on
the creation of the objects of their type as the start() method is invoked within the
constructors themselves.
The output produced by the program on its execution shows that there is co-ordination between
the producer and the consumer classes. We can see that the producer produces the data and the
data is consumed by the consumer. There is no problem in the pattern of production of the data
value and its consumption. We are thus able to achieve the expected producer consumer
relationship between the Producer and the Consumer objects with the use of the
synchronized methods add() and remove() of the DataStock class and with appropriate
the usage of the wait() and notify() methods.

You might also like