<< Previous Chapter | Next Chapter >> |
Multithreading in
Java:
Definition:
Multithreading
is used to achieve multitasking in a java program. Java enables us to develop programs
which have multiple sub programs (threads) which can run concurrently at the
same time.
Few
real examples of multi tasking like washing machine is able to wash and dry at
the same time, we use gas stove which allows us parallel cooking at a same
time, while browsing in internet we can open multiple pages at the same time, a media player plays the audio and show video in
parallel.
In every
program there is a situation which makes process idle for a time period.
Multiprocessing
is a heavy weight multitasking whereas multithreading is a light weight
multitasking. It means the threads are not a separate process; it is actually a
sub program which runs under its parent process. There is no separate memory
allocation for thread; it gets allocated inside its parent process memory area.
For example
the spell checker and auto saving in msword runs in parallel, they are separate
threads which belong to the msword process. If technology would have put them
in separate process instead of putting in a thread it would have taken lots of
switching time and memory. In multitasking the context switching is more than
multithreading because in multitasking all processes are having separate memory
area.
The set
of instructions which are not occupying separate memory area, those instructions
are forming a light weight process known as thread. So, the purpose is to
achieve multithreading in java program. It also increase performance of our program,
as idle time is very low and light weight processes runs concurrently. As we
have discussed already about the garbage collection in java, the garbage
collection is a separate thread in java which runs in parallel and look for the
unreachable objects.
As we
know everything in java is represented as an object, the thread is also
represented as an object. Number of thread objects = number of threads.
Rule1 – one object is responsible to start
only thread, if we will try to run more than one thread from a single object,
it will throw exception.
Fig. a high level thread process flow diagram.
We can never create main
thread; we always create child threads only because main threads are created by
JVM.
Lets run this small example
to print current thread name,
package
dev21century.threading;
public class Temp {
public static void main(String[] args)
{
Thread
thread = Thread.currentThread();
System.out.println("Current
Thread Name: " + thread.getName());
System.out.println("Current
Thread Id: " + thread.getId());
}
}
Output:
There are two standard
ways to create threads in java, first way is extending Thread class and second way is by implementing
Runnable interface. There is a third way using Executors which we will discuss in
a separate chapter Java Concurrency Package.
package
dev21century.threading;
public class Thread1 extends Thread
{
Thread1(String
threadName)
{
super(threadName);
}
public void run()
{
for(int i = 0;i<5;i++)
{
System.out.println("Thread Name:
" +
getName());
}
}
public static void main(String args[])
{
Thread1
thread1 = new Thread1("MyThread1");
thread1.start();
}
}
Output:
Rule1
– If we want to
perform common type of tasks then create one thread and multiple objects.
Rule2
– whenever we want to
perform a separate task on each thread then always keep separate thread class
for each object.
Example:
Create a java file MainThread.java in dev21century.threading package:
package
dev21century.threading;
//thread 1 class
class Thread1 extends Thread
{
Thread1(String
threadName)
{
super(threadName);
}
//run method is the
main code of thread
public void run()
{
for(int i = 0;i<5;i++)
{
System.out.println("Thread Name:
" +
getName());
}
}
}
//thread 2 class
class Thread2 extends Thread
{
Thread2(String
threadName)
{
super(threadName);
}
public void run()
{
for(int i = 0;i<10;i++)
{
System.out.println("Thread Name:
" +
getName());
}
}
}
//thread 3 class
class Thread3 extends Thread
{
Thread3(String
threadName)
{
super(threadName);
}
public void run()
{
for(int i = 0;i<20;i++)
{
System.out.println("Thread Name:
" +
getName());
}
}
}
public class MainThread {
public static void main(String[] args)
{
Thread1
thread1 = new Thread1("MyThread1");
Thread2
thread2 = new Thread2("MyThread2");
Thread3
thread3 = new Thread3("MyThread3");
thread1.start();
thread2.start();
thread3.start();
for(int i = 0;i<20;i++)
{
System.out.println(Thread.currentThread().getName());
}
}
}
Output:
Thread Name: MyThread2
Thread Name: MyThread3
main
Thread Name: MyThread1
Thread Name: MyThread2
Thread Name: MyThread3
main
Thread Name: MyThread1
Thread Name: MyThread2
Thread Name: MyThread3
main
Thread Name: MyThread1
Thread Name: MyThread2
Thread Name: MyThread3
main
Thread Name: MyThread1
Thread Name: MyThread2
Thread Name: MyThread3
main
Thread Name: MyThread1
Thread Name: MyThread2
Thread Name: MyThread3
main
Thread Name: MyThread2
Thread Name: MyThread3
main
Thread Name: MyThread3
Thread Name: MyThread2
main
Thread Name: MyThread3
Thread Name: MyThread2
main
Thread Name: MyThread2
Thread Name: MyThread3
main
Thread Name: MyThread3
main
Thread Name: MyThread3
main
Thread Name: MyThread3
main
Thread Name: MyThread3
main
Thread Name: MyThread3
main
Thread Name: MyThread3
main
Thread Name: MyThread3
main
Thread Name: MyThread3
main
Thread Name: MyThread3
main
Thread Name: MyThread3
main
The
output may be different for you, as we are not sure JVM will run which thread
first and how many contexts switching between them.
I have used Thread.sleep(milliseconds)
here so that threads will keep waiting for 500 milliseconds and we can easily
see the output.
Once object of thread
class is created we have to call start() method of thread. The start() method
internally call the run() method. The run() method is the only place where we
can write the code of thread.
Note: we can also call the run method directly,
in such case it will run as a normal method. This is one of the popular
interview questions.
In above program if we
omit the super() statement, then JVM will assign some default name like Thread
-0, Thread-1 etc. if you want you can try this.
Second
way of creating thread using Runnable interface – also called creating thread using
association:
File Name: RunThread.java
package
dev21century.threading;
class NewThread1 implements Runnable
{
int x = 10;
public void run() {
for(int i = 1;i<10;i++)
{
System.out.println("Thread Name: " + Thread.currentThread().getName());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class RunThread {
public static void main(String args[])
{
NewThread1
newThread1 = new NewThread1();
Thread
thread1 = new Thread(newThread1, "MyThread1");
//passing thread
object and thread name
thread1.start();
Thread
thread2 = new Thread(newThread1, "MyThread2");
//passing thread
object and thread name
thread2.start();
}
}
Output:
Thread Name: MyThread1
Thread Name: MyThread2
Thread Name: MyThread1
Thread Name: MyThread2
Thread Name: MyThread1
Thread Name: MyThread2
Thread Name: MyThread1
Thread Name: MyThread2
Thread Name: MyThread1
Thread Name: MyThread2
Thread Name: MyThread1
Thread Name: MyThread2
Thread Name: MyThread1
Thread Name: MyThread2
Thread Name: MyThread2
Thread Name: MyThread1
Thread Name: MyThread2
Thread Name: MyThread1
Your
output may be different.
In this example, inside Thread
class constructor the first argument is of Runnable type, so we are passing our
class object which has implemented the Runnable interface.
Here x is an instance
variable, we have created only one object and passing twice so, variable x may
be overridden. So, rule is if we have any instance variable in the class then it
is recommended create that create separate objects and pass it to the thread
class constructor.
Lets implements above in
a simple program:
package
dev21century.threading;
class NewThread2 implements Runnable
{
int x;
public void run()
{
for(int i
=1;i<=5;i++)
{
System.out.println("Thread
Name: " + Thread.currentThread().getName() + " " + this.x);
try {
Thread.sleep(500);
} catch
(InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class RunThread2 {
public static void main(String[]
args) {
NewThread2
newThread2_1 = new NewThread2();
newThread2_1.x = 50;
Thread
thread1 = new Thread(newThread2_1,"Thread1");
thread1.start();
NewThread2
newThread1_2 = new NewThread2();
newThread1_2.x = 100;
Thread
thread2 = new Thread(newThread1_2, "Thread2");
thread2.start();
NewThread2
newThread1_3 = new NewThread2();
newThread1_3.x = 150;
Thread
thread3 = new Thread(newThread1_3, "Thread3");
thread3.start();
for(int i =
1;i<5;i++)
{
System.out.println("Thread
Name: " + Thread.currentThread().getName());
try{
Thread.sleep(500);
}
catch(Exception e)
{
System.out.println(e);
}
}
}
}
Output:
Thread Name: main
Thread Name: Thread3 150
Thread Name: Thread1 50
Thread Name: Thread2 100
Thread Name: Thread3 150
Thread Name: Thread2 100
Thread Name: main
Thread Name: Thread1 50
Thread Name: Thread3 150
Thread Name: Thread2 100
Thread Name: main
Thread Name: Thread1 50
Thread Name: main
Thread Name: Thread1 50
Thread Name: Thread2 100
Thread Name: Thread3 150
Thread Name: Thread2 100
Thread Name: Thread3 150
Thread Name: Thread1 50
In above program we have
maintained separate objects for all threads and instance variables obviously. The
output may differ for you. Each threads has its own stack, so if thread
completes then all its local variables will be destroyed.
Life Cycle of Thread:
Following steps describes
the complete life cycle of a thread:
- Thread scheduler is a program which provides processor cycle to the threads and changes their state. This program is responsible for moving the threads from one state to the next whenever applicable. It does pooling in new state when thread comes in memory.
- As soon as a thread object is created and started, the scheduler marks the thread as a new and put that in runnable pool (stack), because it is not sure that the processor is free at the time when thread is arrived.
- Now, when processor becomes free, the scheduler picks the thread from runnable stack, mark it as running state and give it to the processor for processing.
- While running there is a chance for any interruption like wait for input, someone called sleep method, join method (will discuss later) etc. in such cases the scheduler marks the thread as waiting and move the thread to the blocked pool, and give chance to other waiting thread.
- Once the thread waiting on blocked state is resumed for some reason like input / output completed, sleep period completed, notify method invoked (will discuss later) etc. then the scheduler again pull the thread from blocking pool and move it to the runnable pool.
- Now, scheduler repeats these steps until thread is completed.
Fig. Life cycle diagram of Thread (Please click on Image to enlarge).
Thread Priority:
In java we have option to
set priority for the threads, always the higher priority thread will be in
running state. There is a method setPriority which is described in coming section.
Constructors:
Thread()
Thread(String
s)
Thread(Runnable
r)
Thread(Runnabel
r, String s)
Thread(ThreadGroup
rg, Runnable r)
Thread(ThreadGroup
rg, Runnable r, String s)
Methods:
public
void setName (String s)
public
String getName ()
public
void setPriority(int priority)
public
int getPriority()
public
void join(long milisecs)
public
void join()
public
void setDaemon(boolean b)
public
boolean isDaemon()
public
void interrupt()
public
boolean isInterrupted()
public
void
suspend()
//deprecated (removed from latest versions)
public
void resume() //deprecated
public
void
stop()
//deprecated
public
static void Thread.currentThread()
public
void yield()
public
void start()
public
boolean isAlive()
1)
setPriority(int priority)
This
method is used to set priority for a thread. It method accepts one of the
following integer arguments:
Thread.MIN_PRIORITY)
1
Thread.
NORM_PRIORITY) 5
Thread.
MAX _PRIORITY) 10
If
we pass value more than 10 then JVM will throw exception.
2)
join()
Lets
take a real example to understand the join concept, few persons having dinner
on dining table and an another person join them, then the table manner says
those people will wait until the newly joined person finish the dinner, even if
they have completed eating.
Exactly
same condition applies for thread as well; in java there is an option for a
thread to join a running thread. In such case the already running thread will
wait for the newly joined thread to finish, even if it has completed its task.
Synchronization
Synchronization is
one of the most important concept in multithreading, we have created a separate
chapter where we will discuss synchronization and other concepts of
multithreading. Please click on next chapter.<< Previous Chapter | Next Chapter >> |
No comments:
Post a Comment