Unit 2

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 35

UNIT-II

Exception:
An Exception is an unwanted event that interrupts the normal flow of the program. When an exception
occurs program execution gets terminated. In such cases we get a system generated error message. The
good thing about exceptions is that they can be handled in Java. By handling the exceptions we can
provide a meaningful message to the user about the issue rather than a system generated message,
which may not be understandable to a user.

Why an exception occurs?

There can be several reasons that can cause a program to throw exception. For example: Opening a non-
existing file in your program, Network connection problem, bad input data provided by user etc.

Exception Handling
If an exception occurs, which has not been handled by programmer then program execution gets
terminated and a system generated error message is shown to the user. For example look at the system
generated exception below:
An exception generated by the system is given below

Exception in thread "main" java.lang.ArithmeticException: / by zero at


ExceptionDemo.main(ExceptionDemo.java:5)
ExceptionDemo : The class name
main : The method name
ExceptionDemo.java : The filename
java:5 : Line number

This message is not user friendly so a user will not be able to understand what went wrong. In order to
let them know the reason in simple language, we handle exceptions. We handle such conditions and
then prints a user friendly warning message to user, which lets them correct the error as most of the
time exception occurs due to bad data provided by user.

Advantage of exception handling


Exception handling ensures that the flow of the program doesn’t break when an exception occurs. For
example, if a program has bunch of statements and an exception occurs mid way after executing certain
statements then the statements after the exception will not execute and the program will terminate
abruptly.
By handling we make sure that all the statements execute and the flow of program doesn’t break.
Difference between error and exception
Errors indicate that something severe enough has gone wrong, the application should crash rather than
try to handle the error.

Exceptions are events that occurs in the code. A programmer can handle such conditions and take
necessary corrective actions. Few examples:
NullPointerException – When you try to use a reference that points to null.
ArithmeticException – When bad data is provided by user, for example, when you try to divide a number
by zero this exception occurs because dividing a number by zero is undefined.
ArrayIndexOutOfBoundsException – When you try to access the elements of an array out of its bounds,
for example array size is 5 (which means it has five elements) and you are trying to access the 10th
element.

Types of exceptions
There are two types of exceptions in Java:
1)Checked exceptions
2)Unchecked exceptions

Checked exceptions

All exceptions other than Runtime Exceptions are known as Checked exceptions as the compiler checks
them during compilation to see whether the programmer has handled them or not. If these exceptions
are not handled/declared in the program, you will get compilation error. For example, SQLException,
IOException, ClassNotFoundException etc.

Unchecked Exceptions

Runtime Exceptions are also known as Unchecked Exceptions. These exceptions are not checked at
compile-time so compiler does not check whether the programmer has handled them or not but it’s the
responsibility of the programmer to handle these exceptions and provide a safe exit. For example,
ArithmeticException, NullPointerException, ArrayIndexOutOfBoundsException etc.

Compiler will never force you to catch such exception or force you to declare it in the method using
throws keyword.
Java Exception Keywords
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.

Try block
The try block contains set of statements where an exception can occur. A try block is always followed by
a catch block, which handles the exception that occurs in associated try block. A try block must be
followed by catch blocks or finally block or both.

Syntax of try block

try{
//statements that may cause an exception
}
While writing a program, if you think that certain statements in a program can throw a exception,
enclosed them in try block and handle that exception

Catch block

A catch block is where you handle the exceptions, this block must follow the try block. A single try block
can have several catch blocks associated with it. You can catch different exceptions in different catch
blocks. When an exception occurs in try block, the corresponding catch block that handles that
particular exception executes. For example if an arithmetic exception occurs in try block then the
statements enclosed in catch block for arithmetic exception executes.

Syntax of try catch in java

try
{
//statements that may cause an exception
}
catch (exception(type) e(object))
{
//error handling code
}

Example: try catch block

If an exception occurs in try block then the control of execution is passed to the corresponding catch
block. A single try block can have multiple catch blocks associated with it, you should place the catch
blocks in such a way that the generic exception handler catch block is at the last(see in the example
below).
The generic exception handler can handle all the exceptions but you should place is at the end, if you
place it at the before all the catch blocks then it will display the generic message. You always want to
give the user a meaningful message for each type of exception rather then a generic message.

class Example1 {
public static void main(String args[]) {
int num1, num2;
try {
/* We suspect that this block of statement can throw
* exception so we handled it by placing these statements
* inside try and handled the exception in catch block
*/
num1 = 0;
num2 = 62 / num1;
System.out.println(num2);
System.out.println("Hey I'm at the end of try block");
}
catch (ArithmeticException e) {
/* This block will only execute if any Arithmetic exception
* occurs in try block
*/
System.out.println("You should not divide a number by zero");
}
catch (Exception e) {
/* This is a generic Exception handler which means it can handle
* all the exceptions. This will execute if the exception is not
* handled by previous catch blocks.
*/
System.out.println("Exception occurred");
}
System.out.println("I'm out of try-catch block in Java.");
}
}
Output:

You should not divide a number by zero


I'm out of try-catch block in Java.

Multiple catch blocks in Java

The example we seen above is having multiple catch blocks, lets see few rules about multiple catch
blocks with the help of examples
1. As I mentioned above, a single try block can have any number of catch blocks.
2. A generic catch block can handle all the exceptions. Whether it is ArrayIndexOutOfBoundsException
or ArithmeticException or NullPointerException or any other type of exception, this handles all of them.

To see the examples of NullPointerException,ArrayIndexOutOfBoundsException,

catch(Exception e){
//This catch block catches all the exceptions
}
If you are wondering why we need other catch handlers when we have a generic that can handle all.
This is because in generic exception handler you can display a message but you are not sure for which
type of exception it may trigger so it will display the same message for all the exceptions and user may
not be able to understand which exception occurred. Thats the reason you should place is at the end of
all the specific exception catch blocks

3. If no exception occurs in try block then the catch blocks are completely ignored.
4. Corresponding catch blocks execute for that specific type of exception:
catch(ArithmeticException e) is a catch block that can hanlde ArithmeticException
catch(NullPointerException e) is a catch block that can handle NullPointerException
5. You can also throw exception,.

Example of Multiple catch blocks

class Example2{
public static void main(String args[]){
try{
int a[]=new int[7];
a[4]=30/0;
System.out.println("First print statement in try block");
}
catch(ArithmeticException e){
System.out.println("Warning: ArithmeticException");
}
catch(ArrayIndexOutOfBoundsException e){
System.out.println("Warning: ArrayIndexOutOfBoundsException");
}
catch(Exception e){
System.out.println("Warning: Some Other exception");
}
System.out.println("Out of try-catch block...");
}
}
Output:

Warning: ArithmeticException
Out of try-catch block...
In the above example there are multiple catch blocks and these catch blocks executes sequentially when
an exception occurs in try block. Which means if you put the last catch block ( catch(Exception e)) at the
first place, just after try block then in case of any exception this block will execute as it can handle
all exceptions. This catch block should be placed at the last to avoid such situations.

Nested try Catch

When a try catch block is present in another try block then it is called the nested try catch block. Each
time a try block does not have a catch handler for a particular exception, then the catch blocks of parent
try block are inspected for that exception, if match is found that that catch block executes.

If neither catch block nor parent catch block handles exception then the system generated message
would be shown for the exception, similar to what we see when we don’t handle exception.

Lets see the syntax first then we will discuss this with an example.

Syntax of Nested try Catch

....
//Main try block
try {
statement 1;
statement 2;
//try-catch block inside another try block
try {
statement 3;
statement 4;
//try-catch block inside nested try block
try {
statement 5;
statement 6;
}
catch(Exception e2) {
//Exception Message
}
}
catch(Exception e1) {
//Exception Message
}

}
//Catch of Main(parent) try block
catch(Exception e3) {
//Exception Message
}
....

Nested Try Catch Example


Here we have deep (two level) nesting which means we have a try-catch block inside a nested try block.
To make you understand better I have given the names to each try block in comments like try-block2,
try-block3 etc.

This is how the structure is: try-block3 is inside try-block2 and try-block2 is inside main try-block, you
can say that the main try-block is a grand parent of the try-block3. Refer the explanation which is given
at the end of this code.

class NestingDemo{
public static void main(String args[]){
//main try-block
try{
//try-block2
try{
//try-block3
try{
int arr[]= {1,2,3,4};
/* I'm trying to display the value of
* an element which doesn't exist. The
* code should throw an exception
*/
System.out.println(arr[10]);
}catch(ArithmeticException e){
System.out.print("Arithmetic Exception");
System.out.println(" handled in try-block3");
}
}
catch(ArithmeticException e){
System.out.print("Arithmetic Exception");
System.out.println(" handled in try-block2");
}
}
catch(ArithmeticException e3){
System.out.print("Arithmetic Exception");
System.out.println(" handled in main try-block");
}
catch(ArrayIndexOutOfBoundsException e4){
System.out.print("ArrayIndexOutOfBoundsException");
System.out.println(" handled in main try-block");
}
catch(Exception e5){
System.out.print("Exception");
System.out.println(" handled in main try-block");
}
}
}
Output:

ArrayIndexOutOfBoundsException handled in main try-block


As you can see that the ArrayIndexOutOfBoundsException occurred in the grand child try-block3. Since
try-block3 is not handling this exception, the control then gets transferred to the parent try-block2 and
looked for the catch handlers in try-block2. Since the try-block2 is also not handling that exception, the
control gets transferred to the main (grand parent) try-block where it found the appropriate catch block
for exception. This is how the the nesting structure works.

Finally

A finally block contains all the crucial statements that must be executed whether exception occurs or
not. The statements present in this block will always execute regardless of whether exception occurs in
try block or not such as closing a connection, stream etc.

Syntax of Finally block

try {
//Statements that may cause an exception
}
catch {
//Handling exception
}
finally {
//Statements to be executed
}

A Simple Example of finally block

Here you can see that the exception occurred in try block which has been handled in catch block, after
that finally block got executed.

class Example
{
public static void main(String args[]) {
try{
int num=121/0;
System.out.println(num);
}
catch(ArithmeticException e){
System.out.println("Number should not be divided by zero");
}
/* Finally block will always execute
* even if there is no exception in try block
*/
finally{
System.out.println("This is finally block");
}
System.out.println("Out of try-catch-finally");
}
}
Output:

Number should not be divided by zero


This is finally block
Out of try-catch-finally

Few Important points regarding finally block

1. A finally block must be associated with a try block, you cannot use finally without a try block. You
should place those statements in this block that must be executed always.

2. Finally block is optional, as we have seen in previous tutorials that a try-catch block is sufficient
for exception handling, however if you place a finally block then it will always run after the execution of
try block.

3. In normal case when there is no exception in try block then the finally block is executed after try
block. However if an exception occurs then the catch block is executed before finally block.

4. An exception in the finally block, behaves exactly like any other exception.

5. The statements present in the finally block execute even if the try block contains control transfer
statements like return, break or continue.

Throw
• In Java, exceptions allows us to write good quality codes where the errors are checked at the
compile time instead of runtime and we can create custom exceptions making the code
recovery and debugging easier.
• The Java throw keyword is used to throw an exception explicitly.
• We specify the exception object which is to be thrown. The Exception has some message with it
that provides the error description. These exceptions may be related to user inputs, server, etc.
• We can throw either checked or unchecked exceptions in Java by throw keyword. It is mainly
used to throw a custom exception
• We can define our own set of conditions or rules and throw an exception explicitly using throw
keyword. For example, we can throw ArithmeticException when we divide number by 5, or any
other numbers, what we need to do is just set the condition and throw any exception using
throw keyword.

Syntax of throw keyword:


throw new exception_class("error message");

For example:
throw new ArithmeticException("dividing a number by 5 is not allowed in this program");

Example of throw keyword


Lets say we have a requirement where we we need to only register the students when their age is less
than 12 and weight is less than 40, if any of the condition is not met then the user should get an
ArithmeticException with the warning message “Student is not eligible for registration”. We have
implemented the logic by placing the code in the method that checks student eligibility if the entered
student age and weight doesn’t met the criteria then we throw the exception using throw keyword.

/* In this program we are checking the Student age


* if the student age<12 and weight <40 then our program
* should return that the student is not eligible for registration.
*/
public class ThrowExample {
static void checkEligibilty(int stuage, int stuweight){
if(stuage<12 && stuweight<40) {
throw new ArithmeticException("Student is not eligible for registration");
}
else {
System.out.println("Student Entry is Valid!!");
}
}

public static void main(String args[]){


System.out.println("Welcome to the Registration process!!");
checkEligibilty(10, 39);
System.out.println("Have a nice day..");
}
}
Output:

Welcome to the Registration process!!Exception in thread "main"


java.lang.ArithmeticException: Student is not eligible for registration
at beginnersbook.com.ThrowExample.checkEligibilty(ThrowExample.java:9)
at beginnersbook.com.ThrowExample.main(ThrowExample.java:18)

Java throws keyword


The Java throws keyword is used to declare an exception. It gives an information to the programmer
that there may occur an exception. So, it is better for the programmer to provide the exception handling
code so that the normal flow of the program can be maintained.

Exception Handling is mainly used to handle the checked exceptions. If there occurs any unchecked
exception such as NullPointerException, it is programmers' fault that he is not checking the code before
it being used.

Syntax of Java throws

1. return_type method_name() throws exception_class_name{


2. //method code
3. }
Which exception should be declared?

Ans: Checked exception only, because:

o unchecked exception: under our control so we can correct our code.


o error: beyond our control. For example, we are unable to do anything if there occurs
VirtualMachineError or StackOverflowError.

Advantage of Java throws keyword

Now Checked Exception can be propagated (forwarded in call stack).

It provides information to the caller of the method about the exception.

Example of throws Keyword

In this example the method myMethod() is throwing two checked exceptions so we have declared these
exceptions in the method signature using throws Keyword. If we do not declare these exceptions then
the program will throw a compilation error.

import java.io.*;
class ThrowExample {
void myMethod(int num)throws IOException, ClassNotFoundException{
if(num==1)
throw new IOException("IOException Occurred");
else
throw new ClassNotFoundException("ClassNotFoundException");
}
}

public class Example1{


public static void main(String args[]){
try{
ThrowExample obj=new ThrowExample();
obj.myMethod(1);
}catch(Exception ex){
System.out.println(ex);
}
}
}
Output: java.io.IOException: IOException Occurred

User-Defined or Custom Exception

Java user-defined exception is a custom exception created and throws that exception using a keyword
‘throw’. It is done by extending a class ‘Exception’. An exception is a problem that arises during the
execution of the program. In Object-Oriented Programming language, Java provides a powerful
mechanism to handle such exceptions. Java allows to create own exception class, which provides own
exception class implementation. Such exceptions are called user-defined exceptions or custom
exceptions. Let us dig deeper and look at how user-defined exceptions are created in Java, its syntax, if
any, and how it is implemented by solving some examples.

Syntax:

We do not have any particular syntax for Java user-defined exception; we will see how to create a User-
defined exception.

Below is the code which will help to Create a User-defined exception class,

class SampleException{
public static void main(String args[]){
try{
throw new UserException(<value>); // used to create new exception and throw
}
catch(Exception e){
System.out.println(e);
}
}
}
class UserException extends Exception{
// code for exception class
}

Here, while creating an exception class, it needs to be extended from java. lang.Exception.

An exception is an event that leads to sudden termination of the program during execution at runtime.

User-defined exception for Invalid ID


class EmployeeException extends Exception
{
public EmployeeException(String s)
{
super(s);
}
}
class SampleEmp
{
void empIDCheck(int EmpID) throws EmployeeException{
if(EmpID<=0 || EmpID>999){
throw new EmployeeException("Invalid Employee ID");
}
}
public static void main(String args[])
{
SampleEmp emp = new SampleEmp();
try
{
emp.empIDCheck(0);
}
catch (EmployeeException e)
{
System.out.println("Exception caught");
System.out.println(e.getMessage());
}
}
}

Output:

Exception caught

Invalid Employee ID

If we are not using throw class here, it would give a compilation error as ‘Unhandled Exception in
method’.

Multi-Threading

Thread
A thread in Java is the direction or path that is taken while a program is being executed. Generally, all
the programs have at least one thread, known as the main thread, that is provided by the JVM or Java
Virtual Machine at the starting of the program’s execution. At this point, when the main thread is
provided, the main() method is invoked by the main thread.

A thread is critical in the program because it enables multiple operations to take place within a single
method. Each thread in the program often has its own program counter, stack, and local variable.

Multithreading vs. Multiprocessing


By formal definition, multithreading refers to the ability of a processor to execute multiple threads
concurrently, where each thread runs a process. Whereas multiprocessing refers to the ability of a
system to run multiple processors concurrently, where each processor can run one or more threads.
Multithreading vs. Multiprocessing — Image by author

From the diagram above, we can see that in multithreading (middle diagram), multiple threads share the
same code, data, and files but run on a different register and stack. Multiprocessing (right diagram)
multiplies a single processor — replicating the code, data, and files, which incurs more overhead.

Multithreading is useful for IO-bound processes, such as reading files from a network or database since
each thread can run the IO-bound process concurrently. Multiprocessing is useful for CPU-bound
processes, such as computationally heavy tasks since it will benefit from having multiple processors;
similar to how multicore computers work faster than computers with a single core.

Note that using multithreading for CPU-bound processes might slow down performance due to
competing resources that ensure only one thread can execute at a time, and overhead is incurred in
dealing with multiple threads.

On the other hand, multiprocessing can be used for IO-bound processes. However, overhead for
managing multiple processes is higher than managing multiple threads as illustrated above. You may
notice that multiprocessing might lead to higher CPU utilization due to multiple CPU cores being used by
the program, which is expected.

Benefits of multithreading

Here are some of the key benefits of multithreading:

• It requires less memory storage.


• Accessing memory is easier since threads share the same parent process.
• Switching between threads is fast and efficient.
• It's faster to generate new threads within an existing process than to create an entirely new
process.
• All threads share one process memory pool and the same address space.
• Threads are more lightweight and have lower overhead.
• The cost of communication between threads is relatively low.
• Creating responsive user interfaces (UIs) is easy.
Benefits of multiprocessing

Here are some of the benefits of multiprocessing:

• It uses simple coding that's easy to understand.


• It helps you overcome global interpreter lock (GIL) limitations in CPython.
• Child processes can be interrupted.
• It completes tasks faster and analyzes large amounts of data.
• It uses multiple CPUs to improve a system's overall power.
• It removes synchronization primitives.
• It's more cost-effective than single processor systems.

Drawbacks of multiprocessing

Here are some of the potential drawbacks associated with multiprocessing:

• It requires more memory storage and overhead than threads to move data between processes.
• Spawning processes take longer than spawning threads.
• An inter-process communication (IPC) model must be implemented to share objects between
processes.
• The entire memory is copied into each subprocess, which can also create more overhead.

Drawbacks of multithreading

Here are some of the potential drawbacks associated with multithreading:

• A multithreading system cannot be interrupted.


• The code can be more challenging to understand.
• The overhead associated with managing different threads may be too costly for basic tasks.
• Debugging and troubleshooting issues may become more challenging because the code can be
complex.

Lifecycle of a Thread in Java

The Life Cycle of a Thread in Java refers to the state transformations of a thread that begins with its birth
and ends with its death. When a thread instance is generated and executed by calling the start() method
of the Thread class, the thread enters the runnable state. When the sleep() or wait() methods of the
Thread class are called, the thread enters a non-runnable mode.

Thread returns from non-runnable state to runnable state and starts statement execution. The thread
dies when it exits the run() process. In Java, these thread state transformations are referred to as the
Thread life cycle.

There are basically 4 stages in the lifecycle of a thread, as given below:

1. New
2. Runnable

3. Running

4. Blocked (Non-runnable state)

5. Dead

➢ New State

As we use the Thread class to construct a thread entity, the thread is born and is defined as being in the
New state. That is, when a thread is created, it enters a new state, but the start() method on the
instance has not yet been invoked.
➢ Runnable State

A thread in the runnable state is prepared to execute the code. When a new thread's start() function is
called, it enters a runnable state.

In the runnable environment, the thread is ready for execution and is awaiting the processor's
availability (CPU time). That is, the thread has entered the queue (line) of threads waiting for execution.

➢ Running State

Running implies that the processor (CPU) has assigned a time slot to the thread for execution. When a
thread from the runnable state is chosen for execution by the thread scheduler, it joins the running
state.

In the running state, the processor allots time to the thread for execution and runs its run procedure.
This is the state in which the thread directly executes its operations. Only from the runnable state will a
thread enter the running state.

➢ Blocked State

When the thread is alive, i.e., the thread class object persists, but it cannot be selected for execution by
the scheduler. It is now inactive.

➢ Dead State

When a thread's run() function ends the execution of sentences, it automatically dies or enters the dead
state. That is, when a thread exits the run() process, it is terminated or killed. When the stop() function
is invoked, a thread will also go dead.

Interrupting a Thread:
If any thread is in sleeping or waiting state (i.e. sleep() or wait() is invoked), calling the interrupt()
method on the thread, breaks out the sleeping or waiting state throwing InterruptedException. If the
thread is not in the sleeping or waiting state, calling the interrupt() method performs normal behaviour
and doesn't interrupt the thread but sets the interrupt flag to true. Let's first see the methods provided
by the Thread class for thread interruption.

interrupt() method: If any thread is in sleeping or waiting for a state then using the interrupt() method,
we can interrupt the execution of that thread by showing InterruptedException. A thread that is in the
sleeping or waiting state can be interrupted with the help of the interrupt() method of Thread class.

Example: Suppose there are two threads and If one of the threads is blocked in an invocation of the
wait(), wait(long), or wait(long, int) methods of the Object class, or of the join(), join(long), join(long,
int), sleep(long), or sleep(long, int), methods of this class, then its interrupt status will be cleared and it
will receive an InterruptedException, which gives the chance to another thread to execute the
corresponding run() method of another thread which results into high performance and reduces the
waiting time of the threads.

Interrupting a thread that doesn’t stop working: In the program, we handle the InterruptedException
using try and catch block, so whenever any thread interrupts the currently executing thread it will
come out from the sleeping state but it will not stop working.

class MyClass extends Thread {

public void run()

try {

for (int i = 0; i < 5; i++) {

System.out.println("Child Thread executing");

// Here current threads goes to sleeping state

// Another thread gets the chance to execute

Thread.sleep(1000);

catch (InterruptedException e) {

System.out.println("InterruptedException occur");

}
class Test {

public static void main(String[] args)

throws InterruptedException

MyClass thread = new MyClass();

thread.start();

// main thread calls interrupt() method on

// child thread

thread.interrupt();

System.out.println("Main thread execution completes");

}}

Output
Main thread execution completes
Child Thread executing
InterruptedException occur

Priority of a Thread (Thread Priority)

Each thread has a priority. Priorities are represented by a number between 1 and 10. In most cases, the
thread scheduler schedules the threads according to their priority (known as preemptive scheduling).
But it is not guaranteed because it depends on JVM specification that which scheduling it chooses. Note
that not only JVM a Java programmer can also assign the priorities of a thread explicitly in a Java
program.

Setter & Getter Method of Thread Priority

Let's discuss the setter and getter method of the thread priority.

public final int getPriority(): The java.lang.Thread.getPriority() method returns the priority of the given
thread.
public final void setPriority(int newPriority): The java.lang.Thread.setPriority() method updates or
assign the priority of the thread to newPriority. The method throws IllegalArgumentException if the
value newPriority goes out of the range, which is 1 (minimum) to 10 (maximum).

3 constants defined in Thread class:


1. public static int MIN_PRIORITY
2. public static int NORM_PRIORITY
3. public static int MAX_PRIORITY

Default priority of a thread is 5 (NORM_PRIORITY). The value of MIN_PRIORITY is 1 and the value of
MAX_PRIORITY is 10.

Example of priority of a Thread:

FileName: ThreadPriorityExample.java

1. // Importing the required classes


2. import java.lang.*;
3.
4. public class ThreadPriorityExample extends Thread
5. {
6.
7. // Method 1
8. // Whenever the start() method is called by a thread
9. // the run() method is invoked
10. public void run()
11. {
12. // the print statement
13. System.out.println("Inside the run() method");
14. }
15.
16. // the main method
17. public static void main(String argvs[])
18. {
19. // Creating threads with the help of ThreadPriorityExample class
20. ThreadPriorityExample th1 = new ThreadPriorityExample();
21. ThreadPriorityExample th2 = new ThreadPriorityExample();
22. ThreadPriorityExample th3 = new ThreadPriorityExample();
23.
24. // We did not mention the priority of the thread.
25. // Therefore, the priorities of the thread is 5, the default value
26.
27. // 1st Thread
28. // Displaying the priority of the thread
29. // using the getPriority() method
30. System.out.println("Priority of the thread th1 is : " + th1.getPriority());
31.
32. // 2nd Thread
33. // Display the priority of the thread
34. System.out.println("Priority of the thread th2 is : " + th2.getPriority());
35.
36. // 3rd Thread
37. // // Display the priority of the thread
38. System.out.println("Priority of the thread th2 is : " + th2.getPriority());
39.
40. // Setting priorities of above threads by
41. // passing integer arguments
42. th1.setPriority(6);
43. th2.setPriority(3);
44. th3.setPriority(9);
45.
46. // 6
47. System.out.println("Priority of the thread th1 is : " + th1.getPriority());
48.
49. // 3
50. System.out.println("Priority of the thread th2 is : " + th2.getPriority());
51.
52. // 9
53. System.out.println("Priority of the thread th3 is : " + th3.getPriority());
54.
55. // Main thread
56.
57. // Displaying name of the currently executing thread
58. System.out.println("Currently Executing The Thread : " + Thread.currentThread().getNam
e());
59.
60. System.out.println("Priority of the main thread is : " + Thread.currentThread().getPriority(
));
61.
62. // Priority of the main thread is 10 now
63. Thread.currentThread().setPriority(10);
64.
65. System.out.println("Priority of the main thread is : " + Thread.currentThread().getPriority(
));
66. }
67. }

Output:

Priority of the thread th1 is : 5


Priority of the thread th2 is : 5
Priority of the thread th2 is : 5
Priority of the thread th1 is : 6
Priority of the thread th2 is : 3
Priority of the thread th3 is : 9
Currently Executing The Thread : main
Priority of the main thread is : 5
Priority of the main thread is : 10

We know that a thread with high priority will get preference over lower priority threads
when it comes to the execution of threads. However, there can be other scenarios where
two threads can have the same priority. All of the processing, in order to look after the
threads, is done by the Java thread scheduler. Refer to the following example to
comprehend what will happen if two threads have the same priority.

Thread Synchronization in Java

The process of allowing multiple threads to modify an object in a sequence is called synchronization. We
can allow multiple threads to modify the objects sequentially only by executing that objects’ mutator
methods logic in sequence from multiple threads. This is possible by using an object locking concept.
Thread Synchronization is a process of allowing only one thread to use the object when multiple threads
are trying to use the particular object at the same time. To achieve this Thread Synchronization we have
to use a java keyword or modifier called “synchronized”. Synchronization in java is the capability to
control the access of multiple threads to any shared resource. Java Synchronization is a better option
where we want to allow only one thread to access the shared resource.

GeneralSyntax:
synchronized(objectidentifier)
{
// Access shared variables and other shared resources;
}
Why use Synchronization?

The synchronization is mainly used to :


1. If you start with at least two threads inside a program, there might be a chance when multiple
threads attempt to get to the same resource.
2. It can even create an unexpected outcome because of concurrency issues.

Types of Synchronization
There are basically two types of synchronization available. They are:
1. Process Synchronization: It means sharing system resources by processes in such a way that,
Concurrent access to shared data is handled thereby minimizing the chance of inconsistent data.
2. Thread Synchronization: It means that every access to data shared between threads is
protected so that when any thread starts operation on the shared data, no other thread is
allowed access until the first thread is done.

Locks in Java
Synchronization is built around an internal entity known as the lock or monitor lock. (The API
specification often refers to this entity simply as a “monitor.”) Locks play a role in both aspects of
synchronization: enforcing exclusive access to an object’s state and establishing happens-before
relationships that are essential to visibility.
Every object has a lock associated with it. By convention, a thread that needs exclusive and 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. A thread is said to own the lock between the time it has acquired the
lock and released the lock. As long as a thread owns a lock, no other thread can acquire the same lock.
The other thread will block when it attempts to acquire the lock.

Reentrant Synchronization
A thread cannot acquire a lock owned by another thread. But a thread can acquire a lock that it already
owns. Allowing a thread to acquire the same lock more than once enables reentrant synchronization.
This describes a situation where synchronized code, directly or indirectly, invokes a method that also
contains synchronized code, and both sets of code use the same lock. Without reentrant
synchronization, synchronized code would have to take many additional precautions to avoid having a
thread cause itself to block.

Understanding the problem without synchronization


In this example, we are not using synchronization and creating multiple threads that are accessing the
display method and producing random output.
public class Synchronization implements Runnable

int tickets = 3;

static int i = 1, j = 2, k = 3;

public void bookticket (String name, int wantedtickets)

{
if (wantedtickets <= tickets)

System.out.println (wantedtickets + " booked to " + name);

tickets = tickets - wantedtickets;

else

System.out.println ("No tickets to book");

public void run ()

String name = Thread.currentThread ().getName ();

if (name.equals ("t1"))

bookticket (name, i);

else if (name.equals ("t2"))

bookticket (name, j);

else

bookticket (name, k);

public static void main (String[]args)

Synchronization s = new Synchronization ();

Thread t1 = new Thread (s);


Thread t2 = new Thread (s);

Thread t3 = new Thread (s);

t1.setName ("t1");

t2.setName ("t2");

t3.setName ("t3");

t1.start ();

t2.start ();

t3.start ();

Output:

In the above program, object s of class Synchronization are shared by all the three running threads(t1,
t2, and t3) to call the shared method(void bookticket). Hence the result is non-synchronized and such a
situation is called a Race condition.

Synchronized keyword
Java synchronized keyword marks a block or a method in a critical section. A critical section is where
only one thread is executing at a time, and the thread holds the lock for the synchronized section. This
synchronized keyword helps in writing concurrent parts of any application. It also protects shared
resources within the block.
To overcome the above Race Condition problem, we must synchronize access to the shared display()
method, making it available to only one thread at a time. This is done by using the keyword
synchronized with display() method, i.e. Synchronized void display(String msg)

Thread Synchronization in Java


There are two types of Thread Synchronization. They are:
1. Mutual Exclusive
2. Cooperation (Inter-Thread Communication)

Mutual Exclusive:
Mutual Exclusive helps keep threads from interfering with one another while sharing data. Mutual
Exclusion can be achieved in three ways in java:
1. Synchronized Method
2. Synchronized Block
3. Static Synchronization
Thread Synchronization using Synchronized Method in Java:
Method level synchronization is used for making a method code thread-safe, i.e. only one thread must
be executing this method code.

Syntax:
<accessmodifier>synchronizedmethod(parameter)
{
//synchronizedcode
}
In the case of the synchronized method, the lock object is:
1. class object – if the given method is static.
2. this object – if the method is non-static. ‘this’ is the reference to the current object in which the
synchronized method is invoked.

Example: Implementation of Synchronized Method in Java


public class Synchronization implements Runnable
{
int tickets = 3;
static int i = 1, j = 2, k = 3;
synchronized void bookticket (String name, int wantedtickets)
{
if (wantedtickets <= tickets)
{
System.out.println (wantedtickets + " booked to " + name);
tickets = tickets - wantedtickets;
}
else
{
System.out.println ("No tickets to book");
}
}
public void run ()
{
String name = Thread.currentThread ().getName ();
if (name.equals ("t1"))
{
bookticket (name, i);
}
else if (name.equals ("t2"))
{
bookticket (name, j);
}
else
{
bookticket (name, k);
}
}

public static void main (String[]args)


{
Synchronization s = new Synchronization ();
Thread t1 = new Thread (s);
Thread t2 = new Thread (s);
Thread t3 = new Thread (s);
t1.setName ("t1");
t2.setName ("t2");
t3.setName ("t3");
t1.start ();
t2.start ();
t3.start ();
}
}
Output:

Thread Synchronization using Synchronized Block in Java:


Block-level synchronization is used for making some amount of method code thread-safe. If we want to
synchronize access to an object of a class or only a part of a method to be synchronized then we can use
the synchronized block for it. It is capable to make any part of the object and method synchronized.
Syntax:
synchronized (object reference expression) {
//code block
}
Example: Implementation of Synchronized Block in java
class A implements Runnable
{
int token = 1;
public void run ()
{
synchronized (this)
{
Thread t = Thread.currentThread ();
String name = t.getName ();
System.out.println (token + ".....alloted to " + name);
token++;
}
}
}
class SynchroBlock
{
public static void main (String[]args)
{
A a1 = new A ();
Thread t1 = new Thread (a1);
Thread t2 = new Thread (a1);
Thread t3 = new Thread (a1);
t1.setName ("t1");
t2.setName ("t2");
t3.setName ("t3");
t1.start ();
t2.start ();
t3.start ();
}
}
Output:

Thread Synchronization using Static Synchronized in Java:


In simple words, a static synchronized method will lock the class instead of the object, and it will lock the
class because the keyword static means: “class instead of instance”. The keyword synchronized means
that only one thread can access the method at a time. And static synchronized mean: Only one thread
can access the class at one time.
Example: Implementation of Static Synchronized in Java
class Table
{
synchronized static void printTable (int n)
{
for (int i = 1; i <= 10; i++)
{
System.out.println (n * i);
try
{
Thread.sleep (400);
}
catch (Exception e)
{
}
}
}
}

class MyThread10 extends Thread


{
public void run ()
{
Table.printTable (1);
}
}

class MyThread21 extends Thread


{
public void run ()
{
Table.printTable (10);
}
}

class MyThread31 extends Thread


{
public void run ()
{
Table.printTable (100);
}
}

class MyThread41 extends Thread


{
public void run ()
{
Table.printTable (1000);
}
}

public class StaticSynchronization


{
public static void main (String t[])
{
MyThread10 t1 = new MyThread10 ();
MyThread21 t2 = new MyThread21 ();
MyThread31 t3 = new MyThread31 ();
MyThread41 t4 = new MyThread41 ();
t1.start ();
t2.start ();
t3.start ();
t4.start ();
}
}
Inter-Thread Communication (Cooperation) in Java:

It is a mechanism of communication between two threads or multiple threads that acts on the common
object (owner). To perform the multiple actions at a time we need Inter-thread communication.
For example online video players, audio players, etc. In both types of players generally, there are two
types of threads: Buffer Thread and Playing Thread.
The buffer Thread is responsible to download content from the server into a buffer memory and
whereas the playing thread is responsible to play content and these actions will be done based on
thread communication only.

Methods in object class to perform Inter-Thread Communication:


1. wait(): This method is used to make the particular Thread wait until it gets a notification. This
method pauses the current thread to the waiting room dynamically.
2. notify(): This method is used to send the notification to one of the waiting thread so that thread
enters into a running state and execute the remaining task. This method wakeup a single thread
into the active state (that acts on the common object).

3. notifyAll(): This method is used to send the notification to all the waiting threads so that all
thread enters into the running state and execute simultaneously. This method wakes up all the
waiting threads that act on the common objects.

Note: All these three methods are available in the Object class which is super most class so we can
access all these threemethods in any class directly without any reference. These methods are mainly
used to perform Inner-Thread Communication. By using these methods we can resolve problems like
Producer-Consumer problems, Reader-Writer problems, etc…..

Example to demonstrate Inner Thread Communication in Java


class MyThread implements Runnable
{
int total;
public void run ()
{
for (int i = 1; i <= 1000; i++)
{
total = total + i;
if (total > 10000)
{
synchronized (this)
{
notify ();
}
}
try
{
Thread.sleep (5);
}
catch (InterruptedException ie)
{
}
}
System.out.println ("User Thread total:" + total);
}
}

public class InnerThreadDemo


{
public static void main (String[]args)
{
MyThread mt = new MyThread ();
Thread t = new Thread (mt);
t.start ();
try
{
synchronized (mt)
{
mt.wait ();
}
}
catch (InterruptedException ie)
{
}
System.out.println ("Main Thread total:" + mt.total);
}
}
Output:

Note: We must call wait(), notify() and notifyAll() methods inside the synchronized blocks or
synchronized methods, otherwise it will throw a Runtime Exception called IllegalMonitorStateException.

What is the difference between notify() and notifyAll()?


notify() wakes up only one thread and it follows thread priority. notifyAll() wakes up multiple threads
and it does not follow thread priority.

Explain the reason why wait(), notify() and notifyAll() belong to the object class instead of the thread
class?
According to the Inter-thread communication rule, the owner must be common to the multiple threads.
In Java, the owner can be any Java class object. To make these methods available in any type of owner,
Java defines those methods in object class because it is a supreme class for all owners.

Producer-Consumer Problem
Problem:
There are two processes, a producer and a consumer, that share a common buffer with a limited size.
The producer “produces” data and stores it in the buffer, and the consumer “consumes” the data,
removing it from the buffer. Having two processes that run in parallel, we need to make sure that the
producer will not put new data in the buffer when the buffer is full and the consumer won’t try to
remove data from the buffer if the buffer is empty.
Solution:
For solving this concurrency problem, the producer and the consumer will have to communicate with
each other. If the buffer is full, the producer will go to sleep and will wait to be notified. After the
consumer will remove some data from the buffer, it will notify the producer, and then, the producer will
start refilling the buffer again. The same process will happen if the buffer is empty, but in this case, the
consumer will wait to be notified by the producer.
If this communication is not done properly, it can lead to a deadlock where both processes will wait for
each other.
Classic Approach:

class ShowRoom
{
int value;
boolean pro_thread = true;
synchronized void produce (int i)
{
if (pro_thread == true)
{
value = i;
System.out.println ("Producer has produced Product " + value);
pro_thread = false;
notify ();
}
try
{
wait ();
}
catch (InterruptedException ie)
{
}
}
synchronized int consume ()
{
if (pro_thread == true)
{
try
{
wait();
}
catch (InterruptedException ie)
{
}
}
pro_thread = true;
notify ();
return value;
}
}

class Producer implements Runnable


{
ShowRoom sr;
Producer (ShowRoom sr)
{
this.sr = sr;
}
public void run ()
{
int i = 1;
for (i = 1; i <= 10; i++)
{
sr.produce (i);
try
{
Thread.sleep (1000);
}
catch (InterruptedException ie)
{
}
}
}
}

class Consumer implements Runnable


{
ShowRoom sr;
Consumer (ShowRoom sr)
{
this.sr = sr;
}
public void run ()
{
while (true)
{
int res = sr.consume ();
System.out.println ("Consumer has taken product " + res);
try
{
Thread.sleep (1000);
}
catch (InterruptedException ie)
{
}
}
}
}

public class ProducerConsumer


{
public static void main (String[]args)
{
ShowRoom sr = new ShowRoom ();
Producer p = new Producer (sr);
Consumer c = new Consumer (sr);
Thread t1 = new Thread (p);
Thread t2 = new Thread (c);
t1.start ();
t2.start ();
}
}

Output:

You might also like