Java Unit3
Java Unit3
This is caused by issues with the JVM or hardware. This is caused by conditions in the program such as invalid input or logic errors.
1. Built-in Exception
Build-in Exception are pre-defined exception classes provided by Java to handle common errors during program
execution. There are tw type of built-in exception in java.
Checked Exceptions
Checked exceptions are called compile-time exceptions because these exceptions are checked at compile-time
by the compiler. Examples of Checked Exception are listed below:
•ClassNotFoundException: Throws when the program tries to load a class at runtime but the class is not found
because it's belong not present in the correct location or it is missing from the project.
•InterruptedException: Thrown when a thread is paused and another thread interrupts it.
•InstantiationException: Thrown when the program tries to create an object of a class but fails because the
class is abstract, an interface or has no default constructor.
•FileNotFoundException: Thrown when the program tries to open a file that does not exist.
Unchecked Exceptions
The unchecked exceptions are just opposite to the checked exceptions. The compiler
will not check these exceptions at compile time. In simple words, if a program throws an
unchecked exception and even if we did not handle or declare it, the program would
not give a compilation error. Examples of Unchecked Exception are listed below:
•ArithmeticException: It is thrown when there is an illegal math operation.
•NullPointerException: It is thrown when we try to use a null object (e.g. accessing its
methods or fields).
TRY : Program statements that you want to monitor for exceptions are contained within a try block.
Throw exceptobj1;
• All other exceptions that a method can throw must be declared in the throws clause. If they are not, a
compile-time error will result.
• This is the general form of a method declaration that includes a throws clause:
{ // body of method }
• Here, exception-list is a comma-separated list of the exceptions that a method can throw.
So far, you have only been catching exceptions that are thrown by the Java run-time system. However, it is
possible for your program to throw an exception explicitly, using the throw statement.
The general form of throw is shown here: Here, ThrowableInstance must be an object of type Throwable or a
subclass of Throwable
Syntax: Throw ThrowableInstance;
Throw e;
• throws is a keyword in Java that is used in the signature of a method to indicate
that this method might throw one of the listed type exceptions.
• The caller to these methods has to handle the exception using a try-catch
block.
• In a program, if there is a chance of raising an exception then the compiler
always warns us about it and we must handle that checked exception,
Otherwise, we will get compile time error saying unreported exception XXX
must be caught or declared to be thrown. To prevent this compile time error
we can handle the exception in two ways:
• By using try catch
• By using the throws keyword
Ex. class Sample { Ex. import [Link].*;
static void fun() throws IllegalAccessException class Main {
{ public static void findFile() throws IOException {
[Link]("Inside fun(). "); File newFile=new File("[Link]");
throw new IllegalAccessException("demo"); FileInputStream stream=new FileInputStream(newFile);
} }
public static void main(String args[])
{ public static void main(String[] args) {
try { try{
fun(); findFile();
} } catch(IOException e){
catch (IllegalAccessException e) { [Link](e);
[Link]("Caught in main."); }
} } } }
Output } OUTPUT : [Link]: [Link] (No such file
Inside fun(). or directory)
Caught in main. If a method does not handle exceptions, the type of exceptions
We can use the throws keyword to delegate the that may occur within it must be specified in the throws clause
responsibility of exception handling to the caller (It may be so that methods further up in the call stack can handle them or
a method or JVM) then the caller method is responsible to specify them using throws keyword themselves.
handle that [Link] below example throwing a The findFile() method specifies that an IOException can be
IllegalAccessException from a method and handling it in the thrown. The main() method calls this method and handles the
main method using a try-catch block. exception if it is thrown.
DIFFERENCE BETWEEN THROW AND THROWS
[Link]. THROW THROWS
1 Java throw keyword is used to explicitly throw an Java throws keyword is used to declare an
exception. exception.
2 Throw is followed by an instance Throws is followed by class
3 Throw is used within the method. Throws is used with the method signature.
4 We cannot throw multiple exceptions. You can declare multiple exceptions.
5 It can throw both checked and unchecked It is only used for checked exceptions.
exceptions. Unchecked exceptions do not require throws
6. The method's caller is responsible for
The method or block throws the exception.
handling the exception.
7. public void myMethod() throws IOException
throw new ArithmeticException("Error");
{}
8. It forces the caller to handle the declared
Stops the current flow of execution immediately.
exceptions.
Finally:
The finally block in java is used to put important codes Ex. class Sample2 {
such as clean up code e.g. closing the file or closing public static void main(String[] args)
the connection. {
The finally block executes whether exception rise or try {
not and whether exception handled or not. [Link]("inside try block");
A finally contains all the crucial statements regardless of // Not throw any exception
the exception occurs or not. [Link](34 / 2);
Syntax : }
try { catch (ArithmeticException e) {
// Protected code [Link]("Arithmetic Exception");
} catch (ExceptionType1 e1) { }
// Catch block finally
} catch (ExceptionType2 e2) { [Link]("finally : i execute
// Catch block always.");
} catch (ExceptionType3 e3) { }} }
// Catch block Output
}finally { inside try block
// The finally block always 17
executes. finally : i execute always.
}
Points To Remember While Using Finally Block
•A catch clause cannot exist without a try statement.
•It is not compulsory to have finally clauses whenever a try/catch block is present.
•The try block cannot be present without either catch clause or finally clause.
•Any code cannot be present in between the try, catch, finally blocks.
•finally block is not executed in case exit() method is called before finally block or a fatal
error occurs in program execution.
•finally block is executed even method returns a value before finally block.
Why Java Finally Block Used?
Java finally block can be used for clean-up (closing) the connections, files opened, streams, etc.
those must be closed before exiting the program.
It can also be used to print some final information.
built in exception
• ArithmeticException
It is thrown when an exceptional condition has occurred in an arithmetic operation.
• ArrayIndexOutOfBoundsException
It is thrown to indicate that an array has been accessed with an illegal index. The index is either negative
or greater than or equal to the size of the array.
• ClassNotFoundException
This Exception is raised when we try to access a class whose definition is not found
• FileNotFoundException
This Exception is raised when a file is not accessible or does not open.
• IOException
It is thrown when an input-output operation failed or interrupted
• InterruptedException
It is thrown when a thread is waiting, sleeping, or doing some processing, and it is interrupted.
• NoSuchFieldException
It is thrown when a class does not contain the field (or variable) specified
• NoSuchMethodException
It is thrown when accessing a method which is not found.
• NullPointerException
This exception is raised when referring to the members of a
null object. Null represents nothing
• NumberFormatException
This exception is raised when a method could not convert a
string into a numeric format.
• RuntimeException
This represents any exception which occurs during runtime.
• StringIndexOutOfBoundsException
It is thrown by String class methods to indicate that an index
is either negative or greater than the size of the string.
Java’s Built-in Exceptions 2. ArrayIndexOutOfBounds Exception
1. Arithmetic exception // Java program to demonstrate
// ArrayIndexOutOfBoundException
// Java program to demonstrate class ArrayIndexOutOfBound_Demo {
public static void main(String args[])
// ArithmeticException
{
class ArithmeticException_Demo { try {
int a[] = new int[5];
public static void main(String args[]) a[6] = 9; // accessing 7th element in an array of
// size 5
{ try {
}
int a = 30, b = 0; catch (ArrayIndexOutOfBoundsException e) {
[Link]("Array Index is Out Of Bounds");
int c = a / b; // cannot divide by zero }
}
[Link]("Result = " + c);
}
} catch (ArithmeticException e) {
Output:
[Link]("Can't divide a number by 0"); } }}
Array Index is Out Of Bounds
Output:Can't divide a number by 0
[Link] :
// Java program to illustrate the 4. FileNotFoundException :
null
1) What is wrong with the following code? Why it 2) What will be the output of the following program?
is showing compilation error?
public class JavaExceptionHandlingQuiz
public class JavaExceptionHandlingQuiz {
1 public static void main(String[] args)
{
2 {
public static void main(String[] args)
3 int i = 1;
{
4
try try
5
{ {
6
[Link]("Try Block"); i++;
7
} }
8
9 catch (Exception e)
[Link]("-----"); {
10
11 i++;
catch (Exception e) }
12
{
13 finally
[Link]("Catch Block");
14 {
}
15
}
i++;
16 }
}
17 [Link](i); }}
View Answer View Answer
There should not be any other statements in between try and 3
catch blocks.
3) What will be the output of the following program? 4) What will be the output of the following program?
public class JavaExceptionHandlingQuiz
{ public class JavaExceptionHandlingQuiz
public static void main(String[] args) {
{ public static void main(String[] args)
try {
{ [Link](1); try
{
int i = 100 / 0; [Link](1);
}
[Link](2); catch (Exception e)
} {
catch (Exception e) [Link](2);
{ }
[Link](3);
} [Link](3);
}}
finally {
View Answer
[Link](4);
1
} }}
3 View Answer
Compile time error. try, catch and finally blocks together form one
unit. There should not be any other statements in between try-catch-
finally blocks.
What is Process?
• A process is an instance of a program that is being
executed.
• When we run a program, it does not execute directly.
• It takes some time to follow all the steps required to
execute the program, and following these execution steps is
known as a process.
• A process can create other processes to perform multiple
tasks at a time; the created processes are known as clone or
child process, and the main process is known as the parent
process.
• Each process contains its own memory space and does not
share it with the other processes.
• It is known as the active entity. A typical process remains
in the below form in memory.
A process in OS can remain in any of the following states:
•NEW: A new process is being created.
•READY: A process is ready and waiting to be allocated to a
processor.
What is Thread?
• A thread is the subset of a process and is also known as the lightweight
process.
• A process can have more than one thread, and these threads are managed
independently by the scheduler.
• All the threads within one process are interrelated to each other.
• Threads have some common information, such as data segment, code segment,
files, etc., that is shared to their peer threads. But contains its own
registers, stack, and counter.
How does thread work?
• Thread is a subprocess or an execution unit within a process. A process can
• Each thread within a process shares the memory and resources of that process
only.
• In reality, only a single thread is executed at a time, but due to fast context
switching between threads gives an illusion that threads are running parallelly.
• Multithreading is a Java feature that enables the concurrent execution of two or more parts
of a program, maximizing CPU utilization.
• By definition, multitasking is when multiple processes share common processing resources such as a
CPU.
• The OS divides processing time not only among different applications, but also among each thread
within an application.
• Each part of such a program is called a thread. So, threads are lightweight processes within a
process.
• Threads allows a program to operate more efficiently by doing multiple things at the same time.
• Threads can be used to perform complicated tasks in the background without interrupting the main
program.
Multithreading -Definition Advantages of Java Multithreading
1) It doesn't block the user because threads
are independent and you can perform
It is a process of executing multiple threads simultaneously.
multiple operations at the same time.
A thread is a lightweight sub-process, the smallest unit of processing
2) You can perform many operations
Threads allows a program to operate more efficiently by doing
together, so it saves time.
multiple things at the same time.
3) Threads are independent, so it doesn't
Threads can be used to perform complicated tasks in the background affect other threads if an exception occurs in
without interrupting the main program. a single thread.
.
Life cycle of a Thread (Thread States)
In Java, a thread always exists in any one of the following states. These states are:
1. New
2. Active
i)Runnable
ii)Running
3. Blocked / Waiting
4. Timed Waiting
5. Terminated
o When a thread has finished its job, then it exists or terminates normally.
o Abnormal termination: It occurs when some unusual events such as an unhandled exception or
segmentation fault.
Life cycle of a thread :
A thread goes through various stages in its life cycle. For example, a thread is born,
started, runs, and then dies. The following diagram shows the complete life cycle of a
thread.
Following are the stages of the life cycle −
•New − A new thread begins its life cycle in the new state. It remains in this state
until the program starts the thread. It is also referred to as a born thread.
•Runnable − After a newly born thread is started, the thread becomes runnable.
A thread in this state is considered to be executing its task.
•Waiting − Sometimes, a thread transitions to the waiting state while the thread
waits for another thread to perform a task. A thread transitions back to the
runnable state only when another thread signals the waiting thread to continue
executing.
•Timed Waiting − A runnable thread can enter the timed waiting state for a specified
interval of time. A thread in this state transitions back to the runnable state when
that time interval expires or when the event it is waiting for occurs.
•Terminated (Dead) − A runnable thread enters the terminated state when it completes
its task or otherwise terminates.
Creating Threads:
Threads can be created by using two mechanisms : Extending the Thread class and Implementing the Runnable
Interface
Step 1
You will need to override run() method available in Thread class. This method provides an entry point
for the thread and you will put your complete business logic inside this method. Following is a simple
syntax of run() method −
public void run( )
Step 2
Once Thread object is created, you can start it by calling start() method, which executes a call to run( )
method. Following is a simple syntax of start() method −
void start( );
Create a Thread by Implementing a Runnable Interface
If your class is intended to be executed as a thread then you can achieve this by implementing a Runnable
interface. You will need to follow three basic steps −
Step 1
As a first step, you need to implement a run() method provided by a Runnable interface. This method provides an
entry point for the thread and you will put your complete business logic inside this method. Following is a simple
syntax of the run() method −
Step 3
Once a Thread object is created, you can start it by calling start() method, which executes a call to run( ) method.
Following is a simple syntax of start() method −(belongs to Thread class)
void start();
Java code for thread creation by extending the Thread public class Multithread {
public static void main(String[] args)
class
{
class MultithreadingDemo extends Thread { int n = 8; // Number of threads
for (int i = 0; i < n; i++) {
public void run() MultithreadingDemo object
= new MultithreadingDemo();
{ try {
[Link]();
// Displaying the thread that is running }
}
[Link](Thread " + [Link]().getId() + " is }
running");
Output
} Thread 15 is running
catch (Exception e) {// Throwing an exception Thread 14 is running
[Link]("Exception is caught");
Thread 16 is running
Thread 12 is running
} }}
Thread 11 is running
// Main Class
Thread 13 is running
Thread 18 is running
Thread 17 is running
// Main Class
class Multithread {
2) Thread creation by implementing the Runnable
Interface public static void main(String[] args)
We create a new class which implements {
[Link] interface and override run() method. int n = 8; // Number of threads
for (int i = 0; i < n; i++) {
// Java code for thread creation by implementing the Runnable Interface Thread object
= new Thread(new MultithreadingDemo());
class MultithreadingDemo implements Runnable { [Link]();
}} }
public void run()
Output
{ try {
Thread 13 is running
// Displaying the thread that is running Thread 11 is running
[Link]( "Thread " + [Link]().getId() Thread 12 is running
+ " is running");
Thread 15 is running
Thread 14 is running
} catch (Exception e) { // Throwing an exception
Thread 18 is running
[Link]("Exception is caught"); }}}
Thread 17 is running
Thread 16 is running
public class TestThread {
public static void main(String args[]) {
ThreadDemo thread1 = new ThreadDemo( "Thread-1");
ThreadDemo thread2 = new ThreadDemo( "Thread-2");
[Link]();
Ex. Create a Thread by Extending a Thread Class [Link]();
class ThreadDemo extends Thread { }
ThreadDemo( String name) { } OUTPUT:
super(name); Thread: Thread-1, State: New
[Link]("Thread: " + name + ", " + "State: New"); Thread: Thread-2, State: New
} Thread: main, State: Start
public void run() { Thread: main, State: Start
[Link]("Thread: " + [Link]().getName() + ", " + "State: Running");Thread: Thread-1, State: Running
for(int i = 4; i > 0; i--) { Thread: Thread-2, State: Running
Thread: Thread-1, 4
[Link]("Thread: " +[Link]().getName() + ", " + i); Thread: Thread-2, 4
} Thread: Thread-1, 3
[Link]("Thread: " + [Link]().getName() + ", " + "State: Dead"); Thread: Thread-2, 3
} Thread: Thread-1, 2
public void start () { Thread: Thread-2, 2
[Link]("Thread: " + [Link]().getName() + ", " + "State: Start"); Thread: Thread-1, 1
[Link](); Thread: Thread-2, 1
} Thread: Thread-1, State: Dead
} Thread: Thread-2, State: Dead
Ex. Create a Thread by Implementing a Runnable Interface
class RunnableDemo implements Runnable { OUTPUT:
private String threadName; Thread: Thread-1, State: New
RunnableDemo( String name) { Thread: Thread-2, State: New
threadName = name; Thread: Thread-2, State: Running
[Link]("Thread: " + threadName + ", " + "State: New"); Thread: Thread-1, State: Running
} Thread: Thread-2, 4
public void run() { Thread: Thread-1, 4
[Link]("Thread: " + threadName + ", " + "State: Running"); Thread: Thread-2, 3
for(int i = 4; i > 0; i--) { Thread: Thread-1, 3
[Link]("Thread: " + threadName + ", " + i); Thread: Thread-2, 2
} Thread: Thread-1, 2
[Link]("Thread: " + threadName + ", " + "State: Dead"); Thread: Thread-2, 1
} Thread: Thread-1, 1
} Thread: Thread-2, State: Dead
public class TestThread { Thread: Thread-1, State: Dead
public static void main(String args[]) {
RunnableDemo runnableDemo1 = new RunnableDemo( "Thread-1");
RunnableDemo runnableDemo2 = new RunnableDemo( "Thread-2");
Thread thread1 = new Thread(runnableDemo1);
Thread thread2 = new Thread(runnableDemo2);
[Link]();
[Link](); }}
Thread Class vs Runnable Interface
• If we extend the Thread class, our class cannot extend any other class because Java doesn’t support
multiple inheritance. But, if we implement the Runnable interface, our class can still extend other base
classes.
• We can achieve basic functionality of a thread by extending Thread class because it provides some inbuilt
methods like yield() (forcing a processor to relinquish control of the current running thread), interrupt()
(calling the interrupt() method on the thread, breaks out the sleeping or waiting state) etc. that are not
available in Runnable interface.
• Using runnable will give you an object that can be shared amongst multiple threads.
Thread priorities
Here Threads will have priorities ranging from 1 to 10 and 3 constants are defined as follows:
• public final int getPriority(): [Link]() method returns priority of given thread.
• public final void setPriority(int newPriority): [Link]() method changes the priority
of thread to the value newPriority.
// Java Program to Illustrate Priorities
in Multithreading [Link]("t2 thread priority : " + [Link]());
import [Link].*; [Link]("t3 thread priority : " + [Link]());
// Main class // Setting priorities of above threads by passing integer arguments
class ThreadDemo extends Thread { [Link](2);
public void run() [Link](5);
• Process synchronization
• Thread synchronization
There are two types of thread synchronization mutual exclusive and inter-thread communication.
1. Mutual Exclusive
1. Synchronized method.
2. Synchronized block.
3. Static synchronization.
2. Cooperation (Inter-thread communication in java)
Understanding the problem without Synchronization
(In this example, there is no synchronization, so output is inconsistent.)
class Table{
} class MyThread2 extends Thread{
void printTable(int n) Output:
Table t; 5
{//method not synchronized 100
MyThread2(Table t){ 10
for(int i=1;i<=5;i++) 200
{ [Link](n*i); this.t=t; 15
300
try{ [Link](400); } }public void run(){ 20
400
catch(Exception e) [Link](100); } } 25
{[Link](e);} 500
class TestSynchronization1{
} } }
public static void main(String args[])
class MyThread1 extends Thread{
{ Table obj = new Table();
Table t;
MyThread1 t1=new MyThread1(obj);
MyThread1(Table t){
this.t=t; } MyThread2 t2=new MyThread2(obj);
public void run(){ [Link](); [Link](); } }
class MyThread1 extends Thread{
Synchronized Method Table t;
Synchronized method is used to lock an object for any MyThread1(Table t){
shared resource. this.t=t;
}
When a thread invokes a synchronized method, it
public void run(){
automatically acquires the lock for that object and
releases it when the thread completes its task. [Link](5);
}
Syntax:synchronized public void }
methodName() { } class MyThread2 extends Thread{
//example of java synchronized method Table t;
MyThread2(Table t){
public class TestSynchronization2{
class Table{
this.t=t; public static void main(String args[]){
synchronized void printTable(int n) } Table obj = new Table();//only one object
{//synchronized method public void run(){ MyThread1 t1=new MyThread1(obj);
for(int i=1;i<=5;i++){ [Link](100);
MyThread2 t2=new MyThread2(obj);
[Link](n*i); }
try{ } [Link](); Output:
[Link](400); [Link](); 5 10 15 20 25
}catch(Exception e){[Link](e);} } 100 200 300 400 500
} } } }
Synchronized Block Syntax:synchronized (object reference)
{ // Insert code here}
class Table
• Synchronized block is used to lock an object
for any shared resource. { void printTable(int n){
• Scope of synchronized block is smaller than
synchronized(this) {
the method.
• A Java synchronized block doesn't allow //This is a synchronized block
more than one JVM, to provide access
control to a shared resource. for(int i=1;i<=5;i++){
} [Link](); 100
Table t; 300
this.t=t; 500
[Link](100); } }
class Table
Static Synchronization {
synchronized static void printTable(int n){
The method is declared static in this case. for(int i=1;i<=10;i++){
[Link](n*i);
It means that lock is applied to the class instead of an object try{
and only one thread will access that class at a time. [Link](400);
}catch(Exception e){}
In this example we have used synchronized keyword on the } }}
static method to perform static synchronization. class MyThread1 extends Thread{
Syntax:static synchronized returnType nameOfMethod( public void run(){
Type parameters) { // code } [Link](1);
We don't want interference between t1 and t3 or t2 and t4 bcoz } }
of diff locks. Static synchronization solves this problem. class MyThread2 extends Thread{
public void run(){
[Link](10);
} }
Output:
class MyThread3 extends Thread{ 1
2
public void run(){ 3
4
[Link](100); 5
6
} 7
8
} 9
10
class MyThread4 extends Thread{ 10
20
public void run(){ 30
40
[Link](1000); 50
60
} 70
80
} 90
100
public class TestSynchronization4{ 100
200
public static void main(String t[]){ 300
400
MyThread1 t1=new MyThread1(); 500
600
MyThread2 t2=new MyThread2(); 700
MyThread3 t3=new MyThread3(); 800
900
MyThread4 t4=new MyThread4(); 1000
1000
[Link](); 2000
3000
[Link](); 4000
5000
[Link](); 6000
7000
[Link](); 8000
9000
} } 10000
Interthread Communication
• It is implemented by following methods
of Object class:
o wait()
Method Description
wait() sleep()
The wait() method releases the lock. The sleep() method doesn't release the lock.
It should be notified by notify() or notifyAll() After the specified amount of time, sleep is completed.
methods(belong to the Object class)
Ex. of Inter Thread Communication in Java
synchronized void deposit(int amount){
class Customer{ [Link]("going to deposit...");
int amount=10000; [Link]+=amount;
[Link]("deposit completed... ");
synchronized void withdraw(int amount){ notify();
[Link]("going to withdraw..."); }}
if([Link]<amount){ class Test{
public static void main(String args[]){
[Link]("Less balance; waiting for deposit..."); final Customer c=new Customer();
Thread t = new Thread() {
try{wait();}
//Note: This creates a new anonymous subclass of the
catch(Exception e){ } Thread class.
} public void run() { [Link](15000); }};
[Link]();
[Link]-=amount;
Thread t = new Thread()
[Link]("withdraw completed..."); { public void run() { going to withdraw...
Less balance;
} [Link](10000); waiting for deposit...
}}; going to deposit...
deposit completed..
[Link]();}} withdraw completed
//Producer-Consumer problem ---> Inter Thread
Communication. catch(InterruptedException ie)
class Buffer {
{ [Link]("Exception Caught " +ie);
int item; }
boolean produced = false; }
synchronized void produce(int x) [Link]("Consumer - Consumed " +item);
{ produced = false;
if(produced) notify(); return item;
{ }
try{wait();} }
catch(InterruptedException ie) class Producer extends Thread
{ {
[Link]("Exception Caught"); Buffer b;
}} Producer( Buffer b)
item =x; {this.b = b;
[Link]("Producer - Produced-->" +item); start();}
produced =true; public void run()
notify();} {
synchronized int consume() [Link](10);
{ [Link](20);
if(!produced) [Link](30);
{ [Link](40);
try{wait();} [Link](50);}}
class Consumer extends Thread
{
Buffer b;
Consumer(Buffer b)
{this.b = b;
OUTPUT
start();}
public void run()
{
Producer - Produced-->10
[Link](); Consumer - Consumed 10
[Link]();
[Link](); Producer - Produced-->20
[Link]();
// [Link](); Consumer - Consumed 20
// [Link](); Producer - Produced-->30
// [Link]();
}} Consumer - Consumed 30
public class PCDemo
{public static void main(String args[]) Producer - Produced-->40
{
Buffer b = new Buffer(); //Synchronized Object
Consumer - Consumed 40
Producer p = new Producer(b); Producer - Produced-->50
Consumer c = new Consumer(b);
}}
The two methods of Thread class are
isAlive( ) method returns true if the thread upon which it is called is still
running, otherwise it returns false.
final boolean isAlive( )
join() method waits until the thread on which it is called terminates. The
calling thread waiting until the specified thread joins it.
final void join( ) throws InterruptedException
This method when called from the parent (main) thread makes parent
thread wait till child thread terminates.
final void join( long msec ) throws InterruptedException