Professional Documents
Culture Documents
Introduction
These terms are put here as they are again confusing. All means the same
of doing multiple tasks at the same time. "At the same time" is a
confusing idiom. Many processes may exist whose execution is stopped in
the middle for some reason. Remember, only one can be active at any time.
Which ever process is ready is called and executed. "Many" must be
interpreted this way. It looks all at a time as context switching happens
very fast (like cinema where a number of frames of images are run is such
a speed, our eye cannot catch). Some more confusing terms are Preemptive
and Cooperative Multitasking and Time-sharing . These terms are also
discussed hereunder.
Each type comes with its own advantages. When an immediate action or
attention is required, like keyboard input or a button click, preemptive
is preferred. The disadvantage of cooperative multitasking is, if
designed poorly, it may hang the system.
For forcing the thread to vacate the microprocessor, two properties can
be considered – priority and waiting time. In preemptive scheduling, a
thread with more priority vacates the executing thread of low priority.
That is, low-priority thread yields to the high-priority thread. In
contrary, in non-preemptive scheduling, waiting time is considered and
not priority. That is, the high-priority thread cannot relinquish a low-
priority thread having more waiting time.
Memory Protection
Introduction
If only one process exists before the processor and if the process
execution is stopped for some reason, the microprocessor becomes idle
without any work. This type of programming is known as monolithic
programming. In monolithic programming, the whole process consitutes a
single module. To get a better understanding, Lord Buddha statue in
Hussain Sagar in Hyderabad, India is a monolithic statue (single stone
carvation). The minus point of single stone is if some part is to be
replaced in long future, the whole statue is to be replaced. The same is
the problem with monolithic programming also; if a part of the program is
stopped, the whole program comes to standstill. There comes to the rescue
multithreading.
In the above figure, if the control shifts between the threads t1 and t2
of the same process p1, it is known as lightweight process. If the
control shifts between the threads t1 of process p1 and t1 of process p2,
it is known as heavyweight process. Generally, threads are designed to be
lightweight because heavyweight is a big overhead to the OS.
Advantages of Multithreading
First and foremost advantage is, as you have understood so far,
microprocessor idle time is reduced and consequently output is obtained
faster. When a program is divided into number of threads, each comprising
a few statements, the code becomes simpler, readability increases,
debugging and maintenance becomes easier. Generally, user's input is very
slow in GUI environment. This late user's response is best utilized by
other threads (reduces overall execution time).
Applications of Multithreading/Multiprocessing
Playing media player while doing some activity on the system like typing
a document.
Transferring data to the printer while typing some file.
Downloading some files like images while browsing is going on
(downloading images is very slow).
Running animation while system is busy on some other work.
Honoring requests from multiple clients by an application/web server.
Threads are useful in handling events generated by components like mouse
etc. A thread is capable to produce a frame with images.
Java API support for Threads
The following are common exceptions that come in thread programming. All
are from java.lang package.
After knowing what a thread is, it is the time for creating and using the
thread. Java comes with two approaches for creation of thread.
Aim: To create, start and execute thread by extending Thread class. The
execution should print 0 to 9 numbers with an interval of 1000
milliseconds (1 second).
Once you know thread basics, know how to create threads in Java. Java
comes with two styles of creating threads.
You have seen earlier, the first style and let us go for the second
style.
The following example gives how to create thread and start the thread.
Let us learn through an example.
In the above code, you say d1 is an object of Demo class. You are right;
but at the same time, d1 is a thread of Demo class. It is so because Demo
class extends Thread class. Observe, it is so simple to create a thread
in Demo class. Just extend the class and create an object. The object
becomes thread automatically.
Read the following program and see how simple to create a thread in Java.
The program prints 0 to 9 numbers with a gap of 1000 milliseconds (1
second); it is the simplest way to create and control the thread.
System.out.println("Successful");
}
public static void main(String args[])
{
Demo d1 = new Demo();
d1.start();
}
}
Whenever the thread is started, the first and foremost job it does is
calling the run() method. It is a predefined thread of Thread class. This
method is the heart of the thread and whatever code is expected to run by
the thread should be put here. When the execution of run() method is
over, the thread dies implicitly; naturally, its job is over.
Thread.sleep(1000);
Note : At different times of execution, you may see t2 starts first even
though t1 is started first. It wholly depends on your processor speed and
time availability.
t1.join();
One thread creates the other. The first thread, implicitly created and
executed at the beginning of the process, is main thread. For us, main()
is a method, but internally it is a thread. Do not get confused, there is
a thread group on the name of main; this we get later. In the above
program, main thread created t1 and t2 threads. main thread is known as
parent thread and t1 and t2 are known as child threads. Here, the parent
thread is main and it need not be the main, it can be another thread
also. In multithreaded programming, the programmer should see that child
thread dies first and then its creator, parent thread. If not, t1 and t2
become orphan threads and output may differ (again depends on the
processor speed). Then, how to make the parent thread to die always later
than the child. Call simply join() method on the child thread. join()
method is an instruction to the JVM to see that parent thread should not
be allowed to die before child dies, even if the parent thread completes
its job early.
e.printStackTrace();
After understanding, parent and child threads, who has to die first?
Who ever finish its job, it can die without waiting for the other.
Parent should not die until child dies even though parent finishes its
job earlier.
Child should not die until parent dies even though child finishes its job
earlier.
A learner having OS knowledge and daemon processes etc., will definitely
say the option b – that is, parent should not die until child dies. Even
the parent finishes its job long before, should wait until its child
dies. Child first and then only parent should die. This is correct way of
using threads. Then, how to accomplish this in Java. Very simple to prove
Java is simple, the answer is join() method defined in Thread class.
try
{
t1.join();
}
catch(InterruptedException e)
{
System.out.println(e.getMessage());
}
}
}
t1.join() calling from main() method, instructs JVM not to allow d1 to
die before t1 dies.
Now, tell me what is join() method? Let us generalize our concept. join()
will not allow one thread to die before another. It is a communication
between two threads like between a parent and child.
Still, you require more explanation with a good example where output
difference comes if join() is not used, write in the comments box down.
e.getMessage()
Thread Priorities:::
Introduction
hl2.setPriority(Thread.MIN_PRIORITY+1); // 3
// setting the names
hl1.setName("High"); // for 8 thread
hl2.setName("Less"); // for 3 thread
Thread t1 = Thread.currentThread();
String s1 = t1.getName();
The thread name can also be obtained as follows (as used in earlier
programs).
String s1 = this.getName();
With the following two statements, names are given to threads and these
names are used in run() method to distinguish the threads which is
executing the run() method.
ml1.setName("Less");
ml2.setName("More");
ml1.setPriority(Thread.MIN_PRIORITY+1);
ml2.setPriority(Thread.MAX_PRIORITY-1);
ml1.start(); ml2.start();
Purposefully, the thread with less priority, ml1, is started first. But
still, ml2 starts first that got higher priority. Observe the screenshot.
Generally, the life cycle gives the same meaning for servlets, applets
and threads. Different states, a thread (or applet/servlet) comes under
from its object creation to object removal (garbage collection) is known
as life cycle of thread (or applet/servlet). There are four main states
in the life cycle of thread.
Born state
Runnable state
Blocked state
Dead state
The diagram given below, gives a bird view of the states.
1. Born state
2. Runnable state
When the thread is active, the first and foremost job, it does
implicitly, is calling run() method. When the thread is executing the
run() method, we say, the thread is in runnable state. As it is a
callback method, all the code expected to run by the thread, should be
written here. In this state, the thread is active. This state can be
compared to the paint() method of applets.
3. Blocked state
4. Dead state
A thread can be killed and brought to dead state, anytime from any state,
by calling explicitly stop() method.
Number of ways a thread can be brought to blocked state
Introduction
When the sleep() method time is over, the thread becomes implicitly
active. sleep() method is preferable when the inactive time is known
earlier. Sometimes, the inactive time or blocked time may not be known to
the programmer earlier; to come to the task here comes suspend() method.
The suspended thread will be in blocked state until resume() method is
called on it. These methods are deprecated, as when not used with
precautions, the thread locks, if held, are kept in inconsistent state or
may lead to deadlocks.
Note: You must have noticed, in the earlier sleep() method, that the
thread in blocked state retains all its state. That is, attribute values
remains unchanged by the time it comes into runnable state.
Thread.sleep(1000);
srd2.suspend();
System.out.println("Suspending thread Second");
Thread.sleep(1000);
srd2.resume();
System.out.println("Resuming thread Second");
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
}
Note: When you compile this program, you will get deprecated warning. But
the program works happily.
t1.setPriority(Thread.NORM_PRIORITY+3);
System.out.println("t1 priority after setting: " +
t1.getPriority()); // 8
System.out.println("t1 is daemon by default: " + t1.isDaemon());
// false
t1.setDaemon(true);
System.out.println("After making daemon, t1 is daemon: " +
t1.isDaemon()); // true
Name: Every thread created gets by default a name. The first thread
created gets Thread-0, the next one as Thread-1 and so on. With setName()
method, a thread can be given a name and with getName() method, a
thread's name can be retrieved.
Priority: Every thread created gets by default a priority of 5, known as
NORM_PRIORITY. A thread can be given a priority with setPriority() method
and a thread's priority can be retrieved with getPriority() method.
ThreadGroup: Every thread created should belong to some thread group. A
thread group should be assigned to a thread at the time of creating the
thread itself. If the programmer does not mention any group, the thread
is placed in the default main group. Once a thread group is assigned, it
cannot be reassigned to another group later. ThreadGroup is a class from
java.lang package.
isAlive() method returns a boolean value. A programmer can create a
number of threads of which a few may be active, some inactive and the
other might have dead. To know the state of a thread, a programmer can
use isAlive() method. This method returns true if the thread is in
runnable state or blocked state. In born state and dead state, the method
returns false.
Daemon Threads
The daemon threads are given lowest priority as they should come into
action when no other thread is active as process execution (that gives
output) must be of highest priority. A normal thread can be converted
into a daemon thread with the following simple statement.
t1.setDaemon(true);
Communication between the threads can be done with join() method (between
a parent and a child) and wait() and notify() methods (between a thread
that locked the source and the other waiting for the lock).
Introduction – Basics
Generally, one thread accesses one source of data (or to say, its own
source) like your thread accesses your bank account. Sometimes, it may be
needed multiple threads to access the same source of data like all the
partners of a business may be acting on the joint bank account at the
same time. When multiple threads access or share, there may be data
corruption or inconsistency.
In the above code, first statement is critical and the second is not. For
this reason, in the second snippet, only first statement is synchronized.
This increases the performance to some extent.
An array of 100 threads by name bundle is created. In a for loop they are
initialized and started. In each iteration k is incremented and at the
same time decremented, so that the value of k is unchanged.
Remember, Vector methods are synchronized and ArrayList methods are not.
Similarly, StringBuffer methods are synchronized and SringBuilder methods
are not. Now the programmer has got choice to choose depending on the
code necessity. Synchronization mechanism is very useful to control the
scheduling of threads.
These are the two problems that may creep into the programming code if
the programmer is not aware of these concepts.
We know earlier, every thread should belong to some thread group and if
no group is mentioned, the thread is placed, by default, in main thread
group. When the JVM starts a new process, it creates main thread group
and every thread created by the programmer, if not mentioned any group,
the JVM keeps the thread in the main thread group. The programmer can
explicitly mention a thread group also where the thread is to be placed.
But remember, the thread should be placed in a group at the time of
creating the thread only; else it is placed in main thread group
implicilty. Once a thread is allotted a thread group, throughout its
life, the group cannot be changed.
By placing the thread in a group, the advantage is, the thread attains
the properties of the group implicitly. For example, any thread belonging
to main thread group attains a default priority of 5 because main group
priority is set to 5. The java.lang package includes ThreadGroup class to
manipulate the threads with groups.
Advantages of ThreadGroup
Canceling a Thread
Sometimes, the programmer may require killing a thread from any state.
One example of such a situation is if the thread is taking an unusual
time to complete a task which the programmer cannot bear, it is the time
for him to kill the thread. This is more observed in Web servers. The Web
administrator keeps a timeout period for every task user requests. When
the timeout period is over, he kills the thread to save the resources of
the server.
To cancel a thread, the Thread class includes a method stop(). The stop()
method should be used cautiously because it is going to kill an active
thread. Any lock, the thread if holding, must be cleared first and then
killed; else the data is left inconsistent. For this reason, this method
is deprecated. When you compile the program using stop() method, the
compiler raises a deprecation warning; observe the screenshot.
The following program uses stop() method. The thread is supposed to print
10 numbers, but prints only 6 as stop() method is called after printing 6
numbers.
The following statement kills the thread when the counter i is 7. When
killed, it does not print 7 to 10;
this.stop();
In the rescheduling, some threads may not get a chance of processor for a
long time. These threads are known as starved threads. To request the JVM
to get the processor time (which may not be obliged), the programmer can
use yield() method. The yield() method temporarily stops the execution of
a thread for a while. When stopped, the scheduler recalculates the
scheduling as the order changes. In the recalculation, a starved thread
may get a change of processor time; but not guaranteed. The programmer
cannot force the JVM to allocate the processor time for a starved thread
(like he cannot force garbage collection to take place). It does not
imply any meaning, calling yield() method, when no other thread exist for
execution.
this.yield();
new YieldTest().start();
In the above statement, an anonymous object of YieldTest is created and
start() method is called. This saves memory of an object.
In Java program, you create threads but they are not executed by Java
alone. Java takes the help of the underlying OS to execute them. To
allocate microprocessor time and to supervise all the threads' execution,
the OS comes with Thread Scheduler. The entire responsibility of
maintaining the sequence of execution of threads, where which thread
should be given first preference than the other, lies with the thread
scheduler. The scheduling depends on the algorithm of the scheduler. Many
types of algorithms exist like preemptive and time slicing with round
robin etc. It is a very complex algorithm that executes many times in a
given time.
1. Priority of thread
2. Waiting time of thread
3. Nature of thread
When JVM starts executing a Java program, it creates a few threads for
the execution.
1. main is a method for us, but main is a thread for JVM. The execution
starts with main thread.
4. There is one more, Timer thread, which maintains the time for methods
like sleep() etc.
When a thread is started with start() method, the Java thread joins the
pool of waiting threads. It does not mean the scheduler should give the
processor time immediately. "Joins" means, it is guaranteed of getting
processor time, but at what time depends on the decision of the scheduler
managed by the OS. When a thread joins, the scheduler is executed by the
OS and makes a table of threads' sequence to allocate processor time.
When a sleep() method is called, the thread is removed from the active
pool, then again the scheduler is executed to make the table as there
comes imbalance in the waiting threads. And executed again when the
thread joins the pool when the sleep() time is over. For this reason, the
scheduler executes a number of times when a thread joins, yields, stops
or leaves (when blocked or dies) the pool.
As you can guess, the lightweight process has more performance than
heavyweight process. Generally, threads are designed to be lightweight
process.
t1.start();
t2.start();
try
{
t1.join();
t2.join();
}
catch(InterruptedException e)
{
System.out.println("Some problem. " + e);
}
}
}
ima
In the above statement, five threads test[0], test[1] etc. are not thread
objects; they are reference variables. That is, test[] is an array of
reference variables of Test class but not array objects Test class. They
must be converted into array objects and this is done in the next
statement.
new Test() converts each reference variable into array object. When
converted into object, start() method is called on the thread object so
that it calls the callback method run().
One thread creates another thread. The thread which creates is known as
parent thread and the one getting created is known as child thread. The
question here is who should die first. Is it first parent then child or
other way or who ever finish its job it may die? Any rule exists? Here, I
give an example from our daily life.
A mother walks with her baby child of just 4 years age to nearby
vegetable market by catching baby’s hand. While bargaining with the
vendor, she forgot the child and lost her hand. The child moves while
crying. Get the same child and leave at the same spot. Does she move in
the same direction while crying or in other direction? She moves in a
different direction. If the child does not leave mummy’s hand, the
direction is fixed – from house to market and from market to house. Here,
the child is called as orphan child. The orphan child moves eccentrically
in different direction each time when left alone. So the child should be
with mummy always. If mummy dies first, the child becomes orphan. So, the
child should die first and then the parent.
This above example applies to our threads also. If parent thread dies
first, the child thread becomes orphan thread. Orphan thread moves
eccentrically when called different times. If a parent thread comes to
die (as its assigned job is finished) before its child dies, it should
not be allowed. It is good coding in multithreading. How to do the job?
The answer is join() method. It is an instance method (should be called
with a thread object) defined in Thread class.
Last two join() methods are not much used, where you specify the time
period to wait, because join() method timing is OS dependent where join()
will exactly may not wait the time period you specify.
Calling join() method on a thread, until the thread dies, its parent
thread is not allowed to die by the JVM even it completes its job and
ready for die – a communication between two threads.
Let us see a simple program to know which is parent and which is child.
One parent thread can create lot of child threads (in the above, only one
t1 is created and started) and should wait until all the child threads
die (complete their execution).
The join() method permits one thread to wait until the completion of
another thread.
The join() method waits (does not die) for the death of another thread.
A thread can be in waiting state until other thread terminates its
execution.
In the above code, d1 thread of Demo class is created and started. In the
run() method t1 refers the current thread that is right now executing the
run() method. Obviously, it is d1 as d1 is calling the run() method. The
status of d1 in run() is true and in main() method is false. Calling
join() waits the parent thread main not to die until the child d1 dies.
Because all threads of the process executes within the same memory space
(of process), their execution is faster. A programmer can shift the
control between the threads of the same process and is known as
lightweight process. Similarly, there may be another process with
threads. If the execution control is shifted between the threads of
different processes, it is known as heavyweight process. Obviously,
lightweight threads are faster than heavyweight and for this reason, the
threads are designed mostly as lightweight by the Programmer.
First let us see the two codes of calling start() and run() methods with
screenshots. The observation of screenshots itself tells the difference
and what to be used.
Observe, both the screenshots. With start() method, the thread isAlive()
method returns true and with run() method, the thread isAlive() returns
false. What does it indicates; in both the styles, thread is created and
run() called. With start() style, the JVM implicitly calls run() method
in run() method, the thread d1 being executed. For this isAlive()
returned true. If start() is called, the JVM maintains the life cycle of
thread and does many activities on the thread, thread is eligible.
The daemon threads are given lowest priority as they should come into
action when no other thread is active as process execution (that gives
output) must be of highest priority. A normal thread can be converted
into a daemon thread with the following simple statement.
t1.setDaemon(true);
t1.isDaemon();
When the parent is daemon, the child thread also gets the status of
deamon.
Daemon threads should not be used with regular operations like I/O and
transaction management (which are not atomic) etc.
Daemon threads are executed at the background with supporting tasks (like
handling requests) needed for the execution of normal threads and can be
assigned the responsibility of managing resources.
A process exits after all threads finish their job and only daemon
threads remain.
Daemon threads always given the least priority of 1.
Daemon thread will be exeucted when no other thread is running.
Properties to a thread like setting daemon and priority etc. should be
done before start is called. Once a thread is running, we cannot change
their properties. Sometimes, JVM throws IllegalThreadStateException.
Threads by default are not daemon.
When only daemon threads exist, JVM simply exits without executing
finally blocks and unwind stacks etc.
The sleep() method stops the execution of the right now running thread
(called as current thread) for the specified time. It is declared static
so that it can be called just by class name. "native" means, the sleep()
method execution Java cannot do alone but depends on the underlying
operating system. This method throws a checked exception
"InterruptedException" and should be handled else program does not
compile.
try
{
Thread.sleep(1000);
}
catch(InterruptedException e)
{
System.out.println("Some problem. " + e);
}
The sleep(1000) makes every iteration to pause (stop) by 1000
milliseconds or to say, the thread is inactivated by 1000 milliseconds.
An inactive thread is not eligible for microprocessor time. sleep()
method is declared static so that it need not be called with object and
also, in Thread.sleep(), the Thread represents the current thread. For
this reason, sleep() is declared as static. sleep() method throws a
checked exception "InterruptedException" and is handled in catch block.