Skip to content

Chapter 4 : Exception Handling and Multithreading

Saket Khopkar edited this page Jan 6, 2022 · 3 revisions

Exception Handling

  • Errors are the abnormal situations arise at Compile Time. An error may produce following problems:
  1. Incorrect output
  2. Abrupt termination of the program
  3. System crash
  • Therefore it is important to detect and manage all the possible error conditions in the program.
  • Exceptions are the abnormal situations arise at Run Time.

Types of Errors:

  • Compile Time Errors
  • Run Time Errors (Exceptions)

Compile Time Errors:

  • All syntax errors will be detected and displayed by the Java Compiler and therefore these errors known as Compile Time Errors.
  • Whenever the compiler displays an error, it will not create .class file. It is therefore necessary to fix all errors before we can successfully Compile and Run the program.
  • Most of the Compile Time Errors are due to typing mistakes. Typological errors are hard to find. We may have to check the code word by word or even character by character.
// Compile time error
class Sample
{
    public static void main (String args [ ])
    {
        System.out.println(“Hello Java”) // Missing ;
    }
}
// Output: While compiling above program, compiler displays following error:
// Sample.java : 6: ‘; ‘ expected
// System.out.println (“Hello Java”);
// ^
// 1 error

Common Compile Time Errors:

  • Missing semicolons.
  • Missing (or mismatch of) brackets in classes and methods.
  • Misspelling of identifiers and keywords.
  • Missing double quotes in Strings.
  • Use of undeclared variables.
  • Incompatible types in assignments / initializations.
  • Bad reference to objects.
  • Use of = in place of == operator.
  • Due to incorrect directory path, and so on.

Run Time Errors (Exceptions):

  • Sometimes, program may compile successfully creating the .class file but may not run properly. In such cases Java generates an error message and aborts the program. This is due to exceptions.
  • An exception may produce following problems:
  1. Incorrect output
  2. Abrupt termination of the program
  3. System crash
  • Therefore it is important to detect and manage all the possible exception conditions in the program.
  • Exceptions are the abnormal situations arise at Run Time.
// Example of Run Time Error:
class ExceptionDemo
{
    public static void main (String args [ ])
    {
        int a=10, b=5, c=5;
        int x,y;
        x = a / (b-c); // Division by zero
        System.out.println(“Result x = ”+x);
        y = a / (b+c);
        System.out.println(“Result y = ”+y);
    }
}
// Output: Above program is syntactically correct and therefore does get compile. But while running it displays following message and stop without // running further statements:
// java.lang.ArithmeticException: / by zero
// at ExceptionDemo.main (ExceptionDemo.java:7)

Common Run Time Exceptions:

  • Dividing an integer by zero.
  • Accessing an element that is out of bounds of an array.
  • Accessing a character that is out of bounds of a String.
  • Trying to store a value into an array of an incompatible class or type.
  • Trying to use negative size for an array.
  • Trying to illegally change state of a thread.
  • Trying to cast an instance of a class to one of its subclass.
  • Converting invalid string to a number.
  • Passing a parameter that is not in a valid range or value for a method.
  • Using a null object reference as a legitimate object reference to access method or a variable.

Exceptions:

  • Exception is a run-time error in the program.
  • Exception can be generated by the Java run time system or by manually by the code. Exceptions generated by the Java are related to the fundamental errors that violate the java constraints.
  • When the Java interpreter encounters an exception such as dividing an integer by zero, it creates an exception object and throws it to inform that an exception has occurred.
  • If the exception object is not caught and handled properly, the interpreter will generate an error message and terminate the program.
  • In order to start the execution of remaining program, we should try to catch the exception object thrown by the error condition and then display appropriate message for taking corrective actions.
  • This task is known as exception handling.
  • The purpose of exception handling mechanism is to provide a means to detect and report an “exceptional circumstances” so that appropriate action can be taken.
  • Exception Handling Contains Following Tasks:
  1. Find the problem (Hit the exception).
  2. Inform that an error has occurred (Throw the exception).
  3. Receive the error information (Catch the exception).
  4. Take corrective actions (Handle the exception).
  • Exception Handling Contains Two Parts:
  1. To detect and to throw exception.
  2. To catch and handle exception.

Syntax of Exception Handling Code:

  • Java exception handling is managed by following five keywords:
  1. try:
  • The program statements that to be monitored for exceptions are contained within try block.
  • If an exception occurs within try block, it is thrown.
  • try block can have one or more statements that could generate an exception.
  • If anyone statement generates an exception, the remaining statements in the block are skipped and execution jumps to the catch block that is placed next to try block.
  1. catch:
  • catch block catches the exception thrown by the try block and handle it appropriately.
  • catch block added immediately after the try block.
  • try block must be followed by at least one catch block.
  • catch statement is passed a single parameter. If the catch parameter matches with the type of exception object thrown, then catch block will be executed. Otherwise exception is not caught and the default exception handler will cause execution to terminate.
  1. throw:
  • System generated exceptions are thrown automatically by Java run-time system.
  • To manually throw an exception throw keyword is used.
  • General from of throw is: throw ThrowableInstance
  1. throws:
  • Any exception that is generated out of a method must be specified by a throws clause.
  • It just throws exception and calling method will handle it.
  • If a method can cause an exception that it does not handle, then it must specify it by keyword throws, so that caller of the method can guard themselves against that exception.
  1. finally:
  • Any code that must be executed before a method returns is put in a finally block.
  • It executes after a try/catch block has completed.
  • The finally block will execute whether or not an exception is thrown.
  • We can use it to perform certain house-keeping operations such as closing files and releasing system resources.
  • The finally clause is optional. However, each try statement requires at least one catch or a finally clause.
// Syntax: 
try
{
    statements; // generates an exception
}
catch (Exception-type1 e)
{
    statements; // process the exception-type1
}
catch (Exception-type2 e)
{
    statements; // process the exception-type2
}
catch (Exception-typeN e)
{
    statements; // process the exception-typeN
}
finally
{
    statements; // block of code to be executed before try block ends
}

Java Built-In Exceptions:

  • There are two types of exceptions or Runtime Exceptions:
  1. Unchecked Runtime Exception
  • ArithmeticException :- Arithmetic error, such as divide by zero.
  • ArrayIndexOutOfBoundsException :- Array index is out of bounds.
  • ArrayStoreException :- Assignment to an array element of incompatible data type.
  • ClassCastException :- Invalid Cast.
  • IllegalArgumentException :- Illegal argument used to invoke a method.
  • IllegalMonitorStateException :- Illegal monitor operation, such as waiting on an unlocked thread.
  • IllegalStateException :- Environment or application is in incorrect state.
  • IllegalThreadStateException :- Requested operation not compatible with current thread state.
  • IndexOutOfBoundsException :- Some type of index is out of bounds.
  • NegativeArraySizeException :- Array created with negative size.
  • NullPointerException :- Invalid use of null reference.
  • NumberFormatException :- Invalid conversion of string to a numeric format.
  • SecurityException :- Attempt to violate security.
  • StringIndexOutOfBounds :- Attempt to index outside the bound of the string.
  • UnsupportedOperationException :- Unsupported operation was encountered.
  1. Checked Runtime Exception
  • ClassNotFoundException :- Class not found.
  • CloneNotSupportedException :- Attempt to clone an object that does not implement the Clonable interface.
  • IllegalAccessException :- Access to the class denied.
  • InstantiationException :- Attempt to create an object of an abstract class or interface.
  • InterruptedException :- One thread has been interrupted by another thread.
  • NoSuchFieldException :- A requested field does not exist.
  • NoSuchMethodException :- A requested method does not exist.

Multiple Catch Statement:

  • It is possible to have more than one catch statement in the catch block as illustrated below.
try
{
    statements; // generates an exception
}
catch (Exception-type1 e)
{
    statements; // process the exception-type1
}
catch (Exception-type2 e)
{
    statements; // process the exception-type2
}
|
catch (Exception-typeN e)
{
    statements; // process the exception-typeN
}
  • When an exception in try block is generated, the Java treats the multiple catch statements like case in a switch statement. The first statement whose parameter matches with the exception object will be executed, and the remaining statements will skipped.
  • We can have simply a catch statement with an empty block.
  • Example: catch (Exception e);
  • The catch statement simply ends with a semicolon, which does nothing. This statement will catch an exception and then ignore it.

Using Finally Statement:

  • Java supports finally statement that can be used to handle an exception that is not caught by any of the previous catch statements. Finally block can be used to handle any exception.
  • Any code that must be executed before a method returns is put in a finally block.
  • It executes after a try/catch block has completed.
  • The finally block will execute whether or not an exception is thrown.
  • We can use it to perform certain house-keeping operations such as closing files and releasing system resources.
  • The finally clause is optional. However, each try statement requires at least one catch or a finally clause.

Nested try Statements:

  • The try statement can be nested; i.e. try statement can be written inside another try block.
  • If an inner try statement does not have a catch for a particular exception, then the next try statement’s catch checked for a match. This continues until one of the catch statements match, or until the entire nested try statements are exhausted.
  • If no catch statement matches, then the Java run-time system will handle the exception.
// Program of nested try statements.
class NestTry
{
    public static void main(String args[])
    {
        try
        {
            int a = args.length;
            int b = 42 / a;
            System.out.println("a = " + a);
            try
            {
                if(a==1)
                    a = a/(a-a);
                if(a==2)
                {
                    int c[] = { 1 };
                    c[42] = 99;
                }
            }
            catch(ArrayIndexOutOfBoundsException e)
            {
                System.out.println("Array index out-of-bounds: " + e);
            }
        }
        catch(ArithmeticException e)
        {
            System.out.println("Divide by 0: " + e);
        }
    }
}
// Output: C:\>java NestTry
// Divide by 0: java.lang.ArithmeticException: / by zero
// C:\>java NestTry One
// a = 1
// Divide by 0: java.lang.ArithmeticException: / by zero
// C:\>java NestTry One Two
// a = 2
// Array index out-of-bounds:
// java.lang.ArrayIndexOutOfBoundsException

Program Explanation: If no command-line arguments, a divide-by-zero exception is generated by the outer try block. If one command-line argument, a divide-by-zero exception is generated by the nested try block. Since the inner block does not catch this exception, it is passed on to the outer try block. If two command-line arguments, an array boundary exception is generated from within the inner try block.

Chained Exception:

  • When Java program causes an Exception, then the Exception responds to an exception by throwing another exception. Therefore, the first generated exception causes for throwing another exception in turn. This technique is known as Chained Exception.
  • With the help of chained exception it may be easier to know that when one Exception causes another Exception.
  • Two constructors of Throwable class supports chained exception are listed below:
  1. Throwable (Throwable cause): Parameter cause specifies the actual cause of exception.
  2. Throwable (String str, Throwable cause): String str specifies exception description and cause specifies the actual cause of exception.
  • Two methods of Throwable class that supports the chained exception:
  1. Throwable getCause( ): It returns actual cause of currently generated exception.
  2. Throwable initCause( ): It sets the fundamental exception with invoking exception.
// Program to demonstrate chained exception.
class ChainDemo
{
    public static void main(String args[]) throws Exception
    {
        int n=20, result=0;
        try
        {
            result = n/0;
            System.out.println(“The Result Is “+result);
        }
        catch(ArithmeticException ae)
        {
            System.out.println(“Arithmetic Exception occurred: ”+ae);
            try
            {
                throws new NumberFormatException( );
            }
            catch(NumberFormatException ae)
            {
                System.out.println(“Chained exception thrown manually: “+ae);
            }
        }
    }
}
// Output: Arithmetic Exception occurred:
// java.lang.ArithmeticException: / by zero
// Chained exception thrown manually:
// java.lang.NumberFormatException

Throwing Our Own Exceptions / User Defined Exceptions:

  • We can throw our own user defined exception using the keyword throw.
  • Syntax: throw new Throwable_SubClass;
  • Examples: throw new ArithmeticException (); Throw new NumberFormatException ();
  • Program: Exception is a subclass of Throwable therefore in following program
  • MyException is a subclass of Throwable class. An object of a class that extends Throwable can be thrown and caught.
MyException extends Exception
{
    MyException(String msg)
    {
        super(msg);
    }
}
Class TestMyException
{
    public static void main(String args[ ])
    {
        double x = Double.parseDouble(args[0]);
        double y = Double.parseDouble(args[1]);
        try
        {
            double z = x / y;
            if(z<0.1)
                throw new MyException(“Number is too small”);
            else
                System.out.println(“Result = “ + z);
        }
        catch(MyException me)
        {
            System.out.println(me.getMessage());
        }
    }
}

Introduction to Multithreading:

  • Multithreading is a concept where a program (process) is divided into two or more (multiple) subprograms (processes), which can be implemented at the same time in parallel.
  • It is a form of multitasking. A program that contains multiple flows of control is called as multithreaded program.
  • Multithreading enables to write very efficient programs that makes maximum use of the CPU and kept idle time to a minimum. This is especially important for the interactive, networked environment in which Java operates.
  • Each part of program is called as thread. A thread is similar to a program that has a single flow of control.
  • The program begins, runs through a sequence of executions, and finally ends. At any point of time, there is only one statement under execution.
  • Example: In case of web browser one subprogram can display text, other can display images and some other can display videos and many more from the website.
  • In most of our computers, we have only a single processor and therefore, in reality, the processor is doing only one thing at a time. However processor switches between the processes so fast that it appears to us that all of them being done simultaneously.

Types of Multitasking:

  1. Process based:
  • A process is a program that is executing.
  • Process based multitasking is the feature that allows the computer to run two or more programs concurrently.
  • For example, process based multitasking enables to run Java compiler at the same time we can use the text editor.
  • In process based multitasking program is the smallest unit of code that can be dispatched by the scheduler.
  • Process is a heavy weight task that requires their own address space.
  • Interprocess communication is expensive and limited.
  • Context switching from one process to another is costly.
  • Java programs make the use of process based multitasking environment, but the process based multitasking is not under the control of Java.
  1. Thread based:
  • In a thread based multitasking thread is the smallest unit of dispatch able code. That means single program can perform two or more tasks simultaneously.
  • For example, a text editor can format text at the same time that is printing, as long as the two separate threads are performing these two actions.
  • Thus the process based multitasking deal with big picture, and the thread based multitasking handles the details.
  • Multitasking threads require fewer overheads than multitasking processes.
  • The threads are light weight.
  • They share the same address space.
  • Interthread communication is inexpensive.
  • Context switching from one thread to other thread is low cost.
  • However, the multithreading is multitasking in Java.

Creating Threads:

  • Threads in Java are implemented in the form of objects that contain a method called run ( ). The run ( ) method is the heart and soul of any thread. It makes up the entire body of a Thread and is the only method in which the Thread’s behavior can be implemented.
public void run ( )
{
---------------
--------------- // statements for implementing Thread
---------------
}
  • The run( ) method should be called by an object of the concerned Thread. This can be achieved by creating Thread and initiating it with the help of another Thread method start( ).
  • Ways of creating Threads:
  1. By creating a Thread class (Extending Thread): Define a class that extends Thread class and override its run ( ) method with the code required by the Thread.

  2. By converting a class to a Thread (Implementing Thread): Define a class that implements Runnable interface. The Runnable interface has only one method, run ( ), that is to be defined in the method with the code to be executed by the Thread.

  3. Extending the Thread class

  • We can make our class runnable as thread by extending java.lang.Thread
  • This gives us access to all Thread methods directly.
import java.lang.Thread;
class A extends Thread
{
    public void run()
    {
        for(int i=1;i<=5;i++)
        {
            System.out.println("i = "+i);
        }
        System.out.println("Exit From A");
    }
}
class B extends Thread
{
    public void run()
    {
        for(int j=1;j<=5;j++)
        {
            System.out.println("j = "+j);
        }
        System.out.println("Exit From B");
    }
}
class C extends Thread
{
    public void run()
    {
        for(int k=1;k<=5;k++)
        {
            System.out.println("k = "+k);
        }
        System.out.println("Exit From c");
    }
}
class ThreadDemo
{
    public static void main(String args[])
    {
        new A( ).start( );
        new B( ).start( );
        new C( ).start( );
    }
} 
/* 
Output: i = 1
i = 2
j = 1
k = 1
i = 3
i = 4
i = 5
j = 2
Exit From A
j = 3
k = 2
k = 3
k = 4
k = 5
Exit From C
j = 4
j = 5
Exit From B
*/
  1. Implementing the Runnable Interface:
  • The Runnable interface declares the run() method that is required for implementing threads in our programs.
class A implements Runnable // Step 1
{
    public void run() // Step 2
    {
        for(int i=1;i<=5;i++)
        {
            System.out.println("i = "+i);
        }
        System.out.println("Exit A");
    }
}
class B implements Runnable
{
    public void run()
    {
        for(int j=1;j<=5;j++)
        {
            System.out.println("j = "+j);
        }
        System.out.println("Exit B");
    }
}
class RunDemo
{
    public static void main(String args[])
    {
        A aobj=new A();
        B bobj=new B();
        Thread t1=new Thread(aobj); // Step 3
        t1.start(); // Step 4
        Thread t2=new Thread(bobj);
        t2.start();
    }
}
/*
Output: i = 1
i = 2
j = 1
j = 2
i = 3
i = 4
i = 5
Exit From A
j = 3
j = 4
j = 5
Exit From B
*/

Thread Priority:

  • In Java each thread is assigned a priority, the thread with higher priority will get more chance on CPU as compare to thread with lower priority.
  • Priorities are used to ensure that high priority process will get more attention.
  • If the threads are having same priority then they share the processor on a first-come first-serve basis.

Methods:

  • setPriority( ):
  • Syntax : void setPriority(int number)
  • Description : This method is used to set priority from 1 to 10. It may uses constants.

Constants :

  • MIN_PRIORITY = 1
  • NORM_PRIORITY = 5
  • MAX_PRIORITY = 10
  • These constants are static, so they are accessed using class name.
  • Default priority: Default priority is always NORM_PRIORITY.

getPriority( ):

  • Syntax : int getPriority()
  • Description : This method is used to know the priority of particular thread.

Synchronization:

  • Multithreading introduces an asynchronous behavior to the program, so the synchronicity must be enforced.
  • Threads uses own data and methods which is provided inside their run () method.
  • But sometimes they try to use data and methods outside themselves and it may create serious problem.
  • When two or more threads need to access shared resources, they need some way to ensure that the resource will be used by only one thread at a time. The process by which this is achieved is called synchronization.
  • For example: One thread trying to read records from file while another thread is still writing to the same file. In Java, this problem can be overcome by using synchronization. The keyword synchronized is used.
  • When method is declared as synchronized, Java creates a monitor (semaphore) and hands it over to the thread that calls method first time.
  • A monitor is an object that is used as a mutually exclusive lock, or mutex.
  • Only one thread can own a monitor at a given time. When a thread acquires a lock, it is said to have monitor, no other thread can enter the synchronized section of code.
  • Monitor is similar to a key and the thread that holds there key can only open the lock.
  • The other threads are said to be waiting for the monitor.

Thread Methods:

Thread class methods can be used tom control the behavior of the Thread. It contains following instance methods which can be called using Thread object.

  • public void start ( ) :- Starts the Thread in a separate path of execution, then invokes the run ( ) method on this Thread object.
  • public void run ( ) :- If this thread was constructed using separate Runnable run object, then that Runnable object's run method is called; otherwise, this method does nothing & returns.
  • void destroy( ) :- This method is used to destroy the thread without any cleanup. Any monitors it held would have remained locked. It is deadlock-prone.
  • void suspend() :- It is used to suspend (block) the thread. It is also dead-lock prone.
  • void resume() :- It is used to resume (restart) the suspended thread.
  • void stop() :- It is used to stop the Thread. It will send the thread to dead state.
  • void setName(String name) :- Changes the name of this thread to be equal to the argument name.
  • String getName() :- Returns this thread's name.
  • void setPriority(int newPriority) :- Changes the priority of this thread.
  • int getPriority() :- Returns this thread's priority.
  • void interrupt() :- Interrupts this thread.
  • boolean isAlive() :- Tests if this thread is alive.

Thread class contains following static methods which can be called using Thread class. These methods performs the operation on the currently running Thread.

  • public static void yield ( ) :- Causes the currently running thread to yield to any other threads of the same priority that are waiting to be scheduled.
  • public static void sleep (long milisec) :- Causes the currently running thread to block for at least the specified number of milliseconds.
  • public static boolean holdsLock(Object x) :- Returns true if the current thread holds the lock on the given Object.
  • public static Thread currentThread() :- Returns a reference to the currently running thread, which is the thread that invokes this method.
  • public static void dumpStack() :- Prints the stack trace for the currently running thread, which is useful when debugging a multithreaded application.

Inter Thread Communication:

  • Interprocess communication and inter thread communication is little bit related topics. Inter thread communication is important when you develop an application where two or more threads exchange some information. Thread pooling is avoided by using ITC.

  • There are simply three methods and a little trick which makes thread communication possible.

  • public void wait() :- Causes the current thread to wait until another thread invokes the notify().

  • public void notify() :- Wakes up single thread which is waiting on this object's monitor.

  • public void notifyAll() :- Wakes up all the threads that called wait() on the same object.

  • These methods have been implemented as final methods in Object, so they are available in all the classes. All three methods can be called only from within a synchronized context.