Unit - 4
Exception Handling and Multithreading
The Exception Handling in Java is one of the powerful mechanisms to handle the runtime errors so that the normal flow of the application can be maintained.
In Java, an exception is an event that disrupts the normal flow of the program. It is an object which is thrown at runtime.
What is Exception Handling?
Exception Handling is a mechanism to handle runtime errors such as ClassNotFoundException, IOException, SQLException, RemoteException, etc.
There are mainly two types of exceptions: checked and unchecked. An error is considered as the unchecked exception. However, according to Oracle, there are three types of exceptions namely:
1) Checked Exception:
The classes that directly inherit the Throwable class except RuntimeException and Error are known as checked exceptions.
For example, IOException, SQLException, etc. Checked exceptions are checked at compile-time.
1. ClassNotFoundException: The ClassNotFoundException is a kind of checked exception that is thrown when we attempt to use a class that does not exist.
Checked exceptions are those exceptions that are checked by the Java compiler itself.
2. FileNotFoundException: The FileNotFoundException is a checked exception that is thrown when we attempt to access a non-existing file.
3. InterruptedException: InterruptedException is a checked exception that is thrown when a thread is in sleeping or waiting state and another thread attempt to interrupt it.
4. InstantiationException: This exception is also a checked exception that is thrown when we try to create an object of abstract class or interface. That is, InstantiationException exception occurs when an abstract class or interface is instantiated.
5. IllegalAccessException: The IllegalAccessException is a checked exception and it is thrown when a method is called in another method or class but the calling method or class does not have permission to access that method.
6. CloneNotSupportedException: This checked exception is thrown when we try to clone an object without implementing the cloneable interface.
7. NoSuchFieldException: This is a checked exception that is thrown when an unknown variable is used in a program.
8. NoSuchMethodException: This checked exception is thrown when the undefined method is used in a program.
2) Unchecked Exception
The classes that inherit the RuntimeException are known as unchecked exceptions.
For example, ArithmeticException, NullPointerException, ArrayIndexOutOfBoundsException, etc
Unchecked exceptions are not checked at compile-time, but they are checked at runtime.
Let’s see a brief description of them.
1. ArithmeticException: This exception is thrown when arithmetic problems, such as a number is divided by zero, is occurred. That is, it is caused by maths error.
2. ClassCastException: The ClassCastException is a runtime exception that is thrown by JVM when we attempt to invalid typecasting in the program. That is, it is thrown when we cast an object to a subclass of which an object is not an instance.
3. IllegalArgumentException: This runtime exception is thrown by programmatically when an illegal or appropriate argument is passed to call a method. This exception class has further two subclasses:
- Number Format Exception
- Illegal Thread State Exception
Numeric Format Exception: Number Format Exception is thrown by programmatically when we try to convert a string into the numeric type and the process of illegal conversion fails. That is, it occurs due to the illegal conversion of a string to a numeric format.
Illegal Thread State Exception: Illegal Thread State Exception is a runtime exception that is thrown by programmatically when we attempt to perform any operation on a thread but it is incompatible with the current thread state.
4. IndexOutOfBoundsException: This exception class is thrown by JVM when an array or string is going out of the specified index. It has two further subclasses:
- ArrayIndexOutOfBoundsException
- StringIndexOutOfBoundsException
ArrayIndexOutOfBoundsException: ArrayIndexOutOfBoundsException exception is thrown when an array element is accessed out of the index.
StringIndexOutOfBoundsException: StringIndexOutOfBoundsException exception is thrown when a String or StringBuffer element is accessed out of the index.
5. NullPointerException: NullPointerException is a runtime exception that is thrown by JVM when we attempt to use null instead of an object. That is, it is thrown when the reference is null.
6. ArrayStoreException: This exception occurs when we attempt to store any value in an array which is not of array type. For example, suppose, an array is of integer type but we are trying to store a value of an element of another type.
7. IllegalStateException: The IllegalStateException exception is thrown by programmatically when the runtime environment is not in an appropriate state for calling any method.
8. IllegalMonitorStateException: This exception is thrown when a thread does not have the right to monitor an object and tries to access wait(), notify(), and notifyAll() methods of the object.
9. NegativeArraySizeException: The NegativeArraySizeException exception is thrown when an array is created with a negative size.
3) Error
Error is irrecoverable. Some examples of errors are Out Of Memory Error, Virtual Machine Error, Assertion Error etc.
Difference between Checked and Unchecked Exceptions
Checked Exception | Unchecked Exception |
These exceptions are checked at compile time. These exceptions are handled at compile time too. | These exceptions are just opposite to the checked exceptions. These exceptions are not checked and handled at compile time. |
These exceptions are direct subclasses of exception but not extended from RuntimeException class. | They are the direct subclasses of the RuntimeException class. |
The code gives a compilation error in the case when a method throws a checked exception. The compiler is not able to handle the exception on its own. | The code compiles without any error because the exceptions escape the notice of the compiler. These exceptions are the results of user-created errors in programming logic. |
These exceptions mostly occur when the probability of failure is too high. | These exceptions occur mostly due to programming mistakes. |
Common checked exceptions include IOException, DataAccessException, InterruptedException, etc. | Common unchecked exceptions include ArithmeticException, InvalidClassException, NullPointerException, etc. |
These exceptions are propagated using the throws keyword. | These are automatically propagated. |
It is required to provide the try-catch and try-finally block to handle the checked exception. | In the case of unchecked exception it is not mandatory. |
Java provides five keywords that are used to handle the exception. The following table describes each.
Keyword | Description |
Try | The "try" keyword is used to specify a block where we should place an exception code. It means we can't use try block alone. The try block must be followed by either catch or finally. |
Catch | The "catch" block is used to handle the exception. It must be preceded by try block which means we can't use catch block alone. It can be followed by finally block later. |
Finally | The "finally" block is used to execute the necessary code of the program. It is executed whether an exception is handled or not. |
Throw | The "throw" keyword is used to throw an exception. |
Throws | The "throws" keyword is used to declare exceptions. It specifies that there may occur an exception in the method. It doesn't throw an exception. It is always used with method signature. |
A) try block:
a) Syntax of try block with catch block:
Try {
//Block of statement}
Catch(Exception Handler class)
{
}
b) Syntax of try block with finally block:
Try {
//Block of statement}
Finally {
}
c) Syntax of try block with catchand finally block:
Try {
//Block of statement}
Catch(Exception Handler class)
{
} finally{
}
B) Catch block:
a) Syntax of try block with catch block:
Try {
//Block of statement}
Catch(Exception Handler class)
{
}
b) Multiple catch block:
Syntax of try block with catch block:
Try {
//Block of statement}
Catch(Exception Handlersub class)
{
}catch(Exception Handler super class){
}
C) Nested try block:
Syntax of nested try block
try{ // block of statement
try{
// block of statement
}
Catch(Exception Handler class)
{
}
Catch(Exception Handler class)
{
}
D) Finally:
a) Syntax of finally without catch
Try{
//block of statement
}
Finally {
}
b) Syntax of finally with catch
try {
//Block of statement}
Catch(Exception Handler class)
{
} finally{
}
E) throw:
Syntax:
Throw exception_object;
F) throws:
Syntax:
MethodName () throws comma separated list of exceptionClassNames{}
Difference between throw and throws
Throw keyword | Throws keyword |
|
|
Difference between final, finally and finalize
- The final, finally, and finalize are keywords in Java that are used in exception handling. Each of these keywords has a different functionality.
- The basic difference between final, finally and finalize is that the final is an access modifier, finally is the block in Exception Handling and finalize is the method of object class.
Key | Final | Finally | Finalize |
Definition | Final is the keyword and access modifier which is used to apply restrictions on a class, method or variable. | Finally is the block in Java Exception Handling to execute the important code whether the exception occurs or not. | Finalize is the method in Java which is used to perform clean up processing just before object is garbage collected. |
Applicable to | Final keyword is used with the classes, methods and variables. | Finally block is always related to the try and catch block in exception handling. | Finalize() method is used with the objects. |
Functionality | (1) Once declared, final variable becomes constant and cannot be modified. | (1) finally block runs the important code even if exception occurs or not. | Finalize method performs the cleaning activities with respect to the object before its destruction. |
Execution | Final method is executed only when we call it. | Finally block is executed as soon as the try-catch block is executed. It's execution is not dependant on the exception. | Finalize method is executed just before the object is destroyed. |
- In Java, we can create our own exceptions that are derived classes of the Exception class. Creating our own Exception is known as custom exception or user-defined exception.
- Java custom exceptions are used to customize the exception according to user need.
- Consider the example 1 in which Invalid Age Exception class extends the Exception class.
- Using the custom exception, we can have your own exception and message. Here, we have passed a string to the constructor of superclass i.e. Exception class that can be obtained using getMessage() method on the object we have created.
Following are few of the reasons to use custom exceptions:
- To catch and provide specific treatment to a subset of existing Java exceptions.
- Business logic exceptions: These are the exceptions related to business logic and workflow. It is useful for the application users or the developers to understand the exact problem.
In order to create custom exception, we need to extend Exception class that belongs to java.lang package.
Consider the following example, where we create a custom exception named WrongFileNameException:
Public class WrongFileNameException extends Exception
{
Public WrongFileNameException(String errorMessage)
{ super(errorMessage);
}
}
In Java, multithreading is the process of running several threads at the same time.
A thread is the smallest unit of processing and is a lightweight sub-process. Multitasking is accomplished through the use of multiprocessing and multithreading.
Because threads share memory, we employ multithreading rather than multiprocessing. They conserve memory by not allocating a separate memory space, and context-switching between threads takes less time than processing.
Multithreading in Java is typically utilized in gaming, animation, and other similar applications.
Because Java is a multi-threaded programming language, we can use it to create multi-threaded programs. A multi-threaded program is made up of two or more portions that can operate in parallel, each of which can tackle a distinct task at the same time, maximizing the use of available resources, especially when your computer has several CPUs.
Multitasking is defined as the sharing of common processing resources, such as a CPU, by many processes. Multi-threading extends the concept of multitasking into programs by allowing you to divide certain operations within a single app into several threads. Each thread can execute in its own thread. Not only does the operating system split processing time among distinct apps, but it also divides it among each thread within an application.
Multi-threading allows you to write software in which multiple activities can run at the same time.
Life cycle of a Thread
In its life cycle, a thread travels through several stages. A thread, for example, gets born, starts, runs, and eventually dies. The graphic below depicts a thread's whole life cycle.
Fig: Life cycle of a thread
The stages of the life cycle are listed below.
New – In the new state, a new thread begins its life cycle. It will stay in this state until the thread is started by the program. A born thread is another name for it.
Runnable – When a freshly created thread is initiated, it becomes runnable. When a thread is in this position, it is regarded to be doing its job.
Waiting – When a thread is waiting for another thread to complete a job, it may shift to the waiting state. Only when another thread signals the waiting thread to continue executing does a thread transition back to the runnable state.
Timed waiting – A runnable thread can go into a timed waiting state for a set amount of time. When the time period expires or the event it is waiting for occurs, a thread in this state moves back to the runnable state.
Terminated (Dead) – When a runnable thread completes its task or otherwise terminates, it enters the terminated state.
Advantages of multithreading
- Because threads are autonomous and many operations can be performed at the same time, it does not cause the user to be blocked.
- It saves time by allowing you to do multiple procedures at once.
- Because threads are self-contained, an exception in one thread has no impact on other threads.
Key takeaway
- Multithreading is the process of running several threads at the same time.
- Multithreading in Java is typically utilized in gaming, animation, and other similar applications.
- A multi-threaded program is made up of two or more portions that can operate in parallel, each of which can tackle a distinct task at the same time, maximizing the use of available resources, especially when your computer has several CPUs.
Java provides Thread class to achieve thread programming.
There are two ways to create a thread:
- By extending Thread class
- By implementing Runnable interface.
By extending Thread class:
Thread class provide constructors and methods to create and perform operations on a thread.Thread class extends Object class and implements Runnable interface.
Commonly used Constructors of Thread class:
- Thread()
- Thread(String name)
- Thread(Runnable r)
- Thread(Runnable r,String name)
Commonly used methods of Thread class:
- Public void run(): is used to perform action for a thread.
- Public void start(): starts the execution of the thread.JVM calls the run() method on the thread.
- Public void sleep(long miliseconds): Causes the currently executing thread to sleep (temporarily cease execution) for the specified number of milliseconds.
- Public void join(): waits for a thread to die.
- Public void join(long miliseconds): waits for a thread to die for the specified miliseconds.
- Public int getPriority(): returns the priority of the thread.
- Public int setPriority(int priority): changes the priority of the thread.
- Public String getName(): returns the name of the thread.
- Public void setName(String name): changes the name of the thread.
- Public Thread currentThread(): returns the reference of currently executing thread.
- Public int getId(): returns the id of the thread.
- Public Thread.State getState(): returns the state of the thread.
- Public boolean isAlive(): tests if the thread is alive.
- Public void yield(): causes the currently executing thread object to temporarily pause and allow other threads to execute.
- Public void suspend(): is used to suspend the thread(depricated).
- Public void resume(): is used to resume the suspended thread(depricated).
- Public void stop(): is used to stop the thread(depricated).
- Public boolean isDaemon(): tests if the thread is a daemon thread.
- Public void setDaemon(boolean b): marks the thread as daemon or user thread.
- Public void interrupt(): interrupts the thread.
- Public boolean isInterrupted(): tests if the thread has been interrupted.
- Public static boolean interrupted(): tests if the current thread has been interrupted.
Java Thread Example by extending Thread class
Class Multi extends Thread
{
Public void run()
{
System.out.println("thread is running...");
}
Public static void main(String args[])
{
Multi t1=new Multi();
t1.start();
}
}
OUTPUT:
Thread is running…
By implementing Runnable interface:
The Runnable interface should be implemented by any class whose instances are intended to be executed by a thread.
Runnable interface have only one method named run().
Public void run(): is used to perform action for a thread.
Starting a thread:
Start() method of Thread class is used to start a newly created thread. It performs following tasks:
- A new thread starts(with new callstack).
- The thread moves from New state to the Runnable state.
- When the thread gets a chance to execute, its target run() method will run.
Java Thread Example by implementing Runnable interface
Class Multi3 implements Runnable
{
Public void run()
{
System.out.println("thread is running...");
}
Public static void main(String args[])
{
Multi3 m1=new Multi3();
Thread t1 =new Thread(m1);
t1.start();
}
}
OUTPUT:
Thread is running…
In Java, multithreading is the process of running several threads at the same time.
A thread is the smallest unit of processing and is a lightweight sub-process. Multitasking is accomplished through the use of multiprocessing and multithreading.
Because threads share memory, we employ multithreading rather than multiprocessing. They conserve memory by not allocating a separate memory space, and context-switching between threads takes less time than processing.
Multithreading in Java is typically utilized in gaming, animation, and other similar applications.
Because Java is a multi-threaded programming language, we can use it to create multi-threaded programs. A multi-threaded program is made up of two or more portions that can operate in parallel, each of which can tackle a distinct task at the same time, maximizing the use of available resources, especially when your computer has several CPUs.
Multitasking is defined as the sharing of common processing resources, such as a CPU, by many processes. Multi-threading extends the concept of multitasking into programs by allowing you to divide certain operations within a single app into several threads. Each thread can execute in its own thread. Not only does the operating system split processing time among distinct apps, but it also divides it among each thread within an application.
Multi-threading allows you to write software in which multiple activities can run at the same time.
The Java Thread Model
The Java run-time system depends on threads for many things, and all the class libraries are designed with multithreading in mind. In fact, Java uses threads to enable the entire environment to be asynchronous. This helps reduce inefficiency by preventing the waste of CPU cycles.
The value of a multithreaded environment is best understood in contrast to its counterpart. Single-threaded systems use an approach called an event loop with polling. In this model, a single thread of control runs in an infinite loop, polling a single event queue to decide what to do next. Once this polling mechanism returns with, say, a signal that a network file is ready to be read, then the event loop dispatches control to the appropriate event handler. Until this event handler returns, nothing else can happen in the program. This wastes CPU time. It can also result in one part of a program dominating the system and preventing any other events from being processed. In general, in a single-threaded environment, when a thread blocks (that is, suspends execution) because it is waiting for some resource, the entire program stops running.
The benefit of Java’s multithreading is that the main loop/polling mechanism is eliminated. One thread can pause without stopping other parts of your program. For example, the idle time created when a thread reads data from a network or waits for user input can be utilized elsewhere. Multithreading allows animation loops to sleep for a second between each frame without causing the whole system to pause. When a thread blocks in a Java program, only the single thread that is blocked pauses. All other threads continue to run.
As most readers know, over the past few years, multi-core systems have become commonplace. Of course, single-core systems are still in widespread use. It is important to understand that Java’s multithreading features work in both types of systems. In a single-core system, concurrently executing threads share the CPU, with each thread receiving a slice of CPU time. Therefore, in a single-core system, two or more threads do not actually run at the same time, but idle CPU time is utilized. However, in multi-core systems, it is possible for two or more threads to actually execute simultaneously. In many cases, this can further improve program efficiency and increase the speed of certain operations.
Threads exist in several states. Here is a general description. A thread can be running. It can be ready to run as soon as it gets CPU time. A running thread can be suspended, which temporarily halts its activity. A suspended thread can then be resumed, allowing it to pick up where it left off. A thread can be blocked when waiting for a resource. At any time, a thread can be terminated, which halts its execution immediately. Once terminated, a thread cannot be resumed.
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.
Fig - 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.
Sleep() method
- If a thread do not want to perform any operation for a particular amount of time(just pausing) then we should go for sleep() method
- Whenever we are using sleep() method compulsory we should handle Interrupted Exception otherwise we will get compile time error.
Yield()
- Yield() method causes, to halt present executing thread for giving the chance to rest await threads of same priority
- If there are no waiting threads or all waiting threads have low priority then the same thread will continue it's execution once again.
Syntax:
Public static void yield()
This thread does not return any value
Example:
Class MyThread extends Thread
{
public void run( ) {
for(int i=0;i<10;i++)
{ Thread.yield(); --------------------> 1
System.out.println("child thread");
}
}
}
class ThreadYieldDemo
{ public static void main(String[] args)
{ MyThread t = new MyThread();
t.start();
for(int i=0;i<10;i++){
System.out.println("main thread");
}
}
}
Explanation:
If we are commenting Line1 the both threads will be executed simultaneously & we cannot expat exact execution order If we are not commenting Line1 then the chance of completing main thread is high because child thread always calls yield()
Wait() method
The wait() method causes current thread to release the lock and wait until either another thread invokes the notify() method or the notifyAll() method for this object, or a specified amount of time has elapsed.
The current thread must own this object's monitor, so it must be called from the synchronized method only otherwise it will throw exception.
Method | Description |
Public final void wait()throws Interrupted Exception | It waits until object is notified. |
Public final void wait(long timeout)throws Interrupted Exception | It waits for the specified amount of time. |
Notify() method
The notify() method wakes up a single thread that is waiting on this object's monitor.
If any threads are waiting on this object, one of them is chosen to be awakened. The choice is arbitrary and occurs at the discretion of the implementation.
Syntax: public final void notify()
There are two types of thread synchronization mutual exclusive and inter-thread communication.
- Mutual Exclusive
- Synchronized method.
- Synchronized block.
- Static synchronization.
- Cooperation (Inter-thread communication in java)
Mutual Exclusive
Mutual Exclusive helps keep threads from interfering with one another while sharing data. It can be achieved by using the following three ways:
- By Using Synchronized Method:
If you declare any method as synchronized, it is known as synchronized method.
Synchronized method is used to lock an object for any shared resource.
When a thread invokes a synchronized method, it automatically acquires the lock for that object and releases it when the thread completes its task.
2. By Using Synchronized Block:
Synchronized block can be used to perform synchronization on any specific resource of the method.
Points to Remember
- Synchronized block is used to lock an object for any shared resource.
- Scope of synchronized block is smaller than the method.
- A Java synchronized block doesn't allow more than one JVM, to provide access control to a shared resource.
- The system performance may degrade because of the slower working of synchronized keyword.
- Java synchronized block is more efficient than Java synchronized method.
Syntax
Synchronized (object reference expression)
{
//code block
}
3. By Using Static Synchronization
If you make any static method as synchronized, the lock will be on the class not on object.
Concept of Lock in Java
Synchronization is built around an internal entity known as the lock or monitor.
Every object has a lock associated with it. By convention, a thread that needs consistent access to an object's fields has to acquire the object's lock before accessing them, and then release the lock when it's done with them.
The package java.util.concurrent.locks contains several lock implementations.
References:
1. Herbert Schildt, Java2: The Complete Reference, Tata McGraw-Hill
2. Object Oriented Programming with JAVA Essentilas and Applications, Mc Graw Hill
3. Core and Advanced Java, Black Book- dreamtech
4. Programming with JAVA- E Balagurusamy