0% found this document useful (0 votes)
11 views

Oops Unit 3

Uploaded by

110123104026
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
11 views

Oops Unit 3

Uploaded by

110123104026
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 31

UNIT III

EXCEPTION HANDLING AND MULTITHREADING


Exception Handling basics – Multiple catch Clauses – Nested try Statements – Java’s Built-in
Exceptions – User defined Exception. Multithreaded Programming: Java Thread Model–
Creating a Thread and Multiple Threads – Priorities – Synchronization – Inter Thread
Communic//ation- Suspending –Resuming, and Stopping Threads –Multithreading. Wrappers
– Auto boxing.
3.1 EXCEPTION HANDLING BASICS

What are Exceptions in java?


In Java, Exception is an unwanted or unexpected event, which occurs during the execution of
a program, i.e., at run time, that disrupts the normal flow of the program’s instructions.
Exceptions can be caught and handled by the program. When an exception occurs within a
method, it creates an object. This object is called the exception object. It contains information
about the exception, such as the name and description of the exception and the state of the
program when the exception occurred.
Major reasons why an exception Occurs

• Invalid user input


• Device failure
• Loss of network connection
• Physical limitations (out-of-disk memory)
• Code errors
• Opening an unavailable file

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. It is a mechanism to handle
runtime errors such as ClassNotFoundException, IOException, SQLException,
RemoteException, etc.

Java provides specific keywords for exception handling purposes

i. throw – We know that if an error occurs, an exception object is getting created and then
Java runtime starts processing to handle them. Sometimes we might want to generate
exceptions explicitly in our code. For example, in a user authentication program, we
should throw exceptions to clients if the password is null. The throw keyword is used
to throw exceptions to the runtime to handle it.
ii. throws – When we are throwing an exception in a method and not handling it, then we
have to use the throws keyword in the method signature to let the caller program know
the exceptions that might be thrown by the method. The caller method might handle
these exceptions or propagate them to its caller method using the throws keyword. We
can provide multiple exceptions in the throws clause, and it can be used with the main()
method also.
iii. try-catch – We use the try-catch block for exception handling in our code. try is the
start of the block and catch is at the end of the try block to handle the exceptions. We
can have multiple catch blocks with a try block. The try-catch block can be nested too.
The catch block requires a parameter that should be of type Exception.
iv. finally – the finally block is optional and can be used only with a try-catch block. Since
exception halts the process of execution, we might have some resources open that will
not get closed, so we can use the finally block. The finally block always gets executed,
whether an exception occurred or not.
Example:
import java.util.Scanner;
public class ExceptionHandlingExample {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
try {
System.out.print("Enter a dividend: ");
int dividend = scanner.nextInt();
System.out.println("Enter a divisor: ");
int divisor = scanner.nextInt();
int result = divide(dividend, divisor);
System.out.println("Result: " + result);
} catch (ArithmeticException e) {
System.out.println("An arithmetic exception occurred: " + e.getMessage());
} catch (java.util.InputMismatchException e) {
System.out.println("Invalid input. Please enter valid integers.");
}
finally {
scanner.close();
System.out.println("Finally block executed.");
} }
public static int divide(int dividend, int divisor) {
return dividend / divisor;
}}
Output:
Enter a dividend: 15
Enter a divisor: 3
Result: 5
Finally block executed.
3.2 MULTIPLE CATCH CLAUSES
In some cases, more than one exception could be raised by a single piece of code. To handle
this multiple exceptions, two or more catch clauses can be specified. Here, each catch block
catches different type of exception. When an exception is thrown, each catch statement is
inspected in order, and the first one whose type matches that of the exception is executed. After
one catch statement executes, the others are bypassed, and execution continues after the
try/catch block. The following example traps two different exception types:
class MultiCatch_Example {
public static void main(String args[]) {
try {
int a,b;
a = args.length;
System.out.println(“a = “ + a);
b = 10 / a; //may cause division-by-zero error
int arr[] = { 10,20 };
c[5] =100;
} catch(ArithmeticException e)
{
System.out.println(“Divide by 0: “ + e);
} catch(ArrayIndexOutOfBoundsException e)
{
System.out.println(“Array index oob: “ + e);
}
System.out.println(“After try/catch blocks.”);
}}
Output:
a=0
Divide by 0: java.lang.ArithmeticException: / by zero
After try/catch blocks.
While the multiple catch statements is used, it is important to remember that exception
subclasses must come before their superclasses. A catch statement which uses a superclass will
catch exceptions of that type plus any of its subclasses. Thus, a subclass would never be reached
if it came after its superclass. And also, in Java, unreachable code is an error. For example,
consider the following program:
class MultiCatch_Example {
public static void main(String args[]) {
try {
int a,b;
a = args.length; System.out.println(“a = “ + a);
b = 10 / a; //may cause division-by-zero error int arr[] = { 10,20 };
c[5] =100;
}
catch(Exception e) { System.out.println(“Generic Exception catch.”);
}
catch(ArithmeticException e)
{
System.out.println(“Divide by 0: “ + e);
}
catch(ArrayIndexOutOfBoundsException e)
{
System.out.println(“Array index oob: “ + e);
}
System.out.println(“After try/catch blocks.”);
}}
The exceptions such as ArithmeticException, and ArrayIndexOutOfBoundsException are the
subclasses of Exception class. The catch statement after the base class catch statement is raising
the unreachable code exception.
3.3 NESTED TRY STATEMENTS
In Java, you can use nested try statements to handle exceptions in a more granular way. Nested
try statements involve placing one try-catch block inside another. This allows you to catch
specific exceptions at different levels of your code's execution. Every statement that we enter
a statement in try block, context of that exception is pushed onto the stack. Sometimes a
situation may arise where a part of a block may cause one error and the entire block itself may
cause another error. In such cases, exception handlers have to be nested.
Syntax:
//main try block
try
{
statement 1;
statement 2;
//try catch block within another try block
try
{
statement 3;
statement 4;
//try catch block within nested try block
try
{
statement 5;
statement 6;
}
catch(Exception e2)
{
//exception message
} }
catch(Exception e1)
{
//exception message
}
}
//catch block of parent (outer) try block
catch(Exception e3)
{
//exception message
}
Example 1:
public class NestedTryExample {
public static void main(String[] args) {
try {
// Outer try block
int[] numbers = {1, 2, 3};
try {
// Inner try block
System.out.println(numbers[5]);
//This will throw an ArrayIndexOutOfBoundsException
} catch (ArrayIndexOutOfBoundsException innerException) {
System.out.println("Inner Catch Block: Array index out of bounds");
}
int result = numbers[0] / 0;
// This will throw an ArithmeticException
}
catch (ArithmeticException outerException) {
System.out.println("Outer Catch Block: Arithmetic exception");
} } }
Output:
Inner Catch Block: Array index out of bounds
Outer Catch Block: Arithmetic exception
Example 2: There are three statements in a try block–statement1, statement2 and
statement3. After that there is a catch block to catch the exceptions occurred in the try
block. Assume that exception has occurred in statement2. Does statement3 get executed
or not?
In Java, a try-catch block is used to handle exceptions. It consists of a `try` block where
potentially risky code is placed, followed by one or more `catch` blocks that handle specific
exceptions that might occur in the `try` block. The `catch` block corresponding to the caught
exception is executed if an exception occurs.
When an exception occurs in a `try` block, the control is transferred to the corresponding
`catch` block, and the remaining statements after the point where the exception occurred are
skipped. This includes any statements within the same `try` block that appear after the point of
exception occurrence.
try {
// statement1
// statement2 (exception occurs here)
// statement3 (will not get executed if exception occurred in statement2)
} catch (Exception e) {
// Exception handling code
}
In this scenario, if an exception occurs in `statement2`, the control will immediately jump to
the `catch` block, and `statement3` will not be executed.
If it is crucial to execute a particular statement regardless of whether an exception occurs or
not, the statement should be placed in a `finally` block:
try {
// statement1
// statement2 (exception occurs here)
} catch (Exception e) {
// Exception handling code
} finally {
// statement3 (will be executed regardless of whether an exception occurred)
}
In this case, `statement3` will always be executed, even if an exception occurs in `statement2`.
To summarize, when an exception occurs in a `try` block, the control is transferred to the
corresponding `catch` block, and any subsequent statements within the same `try` block are not
executed. To ensure the execution of specific statements regardless of exceptions, use the
`finally` block.
3.4 JAVA’S BUILT-IN EXCEPTION
Java provides a comprehensive set of built-in exceptions that cover a wide range of error
conditions that can occur during program execution. These exceptions are part of the Java
standard library and are organized in a hierarchy, with the base class java.lang.Throwable being
the superclass of all exceptions. Here are some commonly used built-in exception classes:
1. ArithmeticException: Thrown when an arithmetic operation encounters an error, such
as division by zero.
2. NullPointerException: Thrown when you attempt to access or call methods on an object
that is null.
3. ArrayIndexOutOfBoundsException: Thrown when trying to access an array element at
an invalid index.
4. IndexOutOfBoundsException: A more general version of
ArrayIndexOutOfBoundsException, thrown when an index is out of bounds for a
collection or sequence.
5. IllegalArgumentException: Thrown when a method receives an argument of an
inappropriate type or value.
6. NumberFormatException: Thrown when trying to convert a string to a numeric type,
but the string does not have the appropriate format.
7. ClassCastException: Thrown when an object cannot be cast to a specific type.
8. FileNotFoundException: Thrown when trying to access a file that doesn't exist.
9. IOException: A general exception for input/output-related errors.
10. InterruptedException: Thrown when a thread is interrupted while it's waiting, sleeping,
or otherwise occupied.
11. NoSuchElementException: Thrown by collection classes when trying to access an
element that doesn't exist.
12. RuntimeException: The base class for exceptions that can occur during runtime but are
unchecked, meaning they don't need to be declared in a method's signature.
3.4.1 USER DEFINED EXCEPTION
User-defined exceptions are also referred to as custom exceptions. The exceptions created per
our use case and thrown using the throw keyword are user-defined exceptions, and such
exceptions are derived classes of the Exception class from the java.lang package.
1. How to create a user defined exception:
You can define your own exception class by extending an existing exception class or the
Exception class itself.
public class MyCustomException extends Exception {
public MyCustomException(String message) {
super(message);
}
}
In this example, MyCustomException is a user-defined exception class that extends the
Exception class. You can add constructors to customize the exception's behavior.
2. Throw the User-Defined Exception:
Use the throw keyword to throw an instance of your custom exception class when a specific
condition is met.
public class CustomExceptionExample {
public static void main(String[] args) {
try {
int value = 10;
if (value > 5) {
throw new MyCustomException("Value is greater than 5.");
}
} catch (MyCustomException e) {
System.out.println("Custom Exception: " + e.getMessage());
}
}
} In this example, the MyCustomException is thrown when the value is greater than 5. The
catch block catches the exception and prints the message.
3. Handle the User-Defined Exception:
When you throw a custom exception, it can be caught using a catch block just like built-in
exceptions.
try {
// Code that might throw MyCustomException
} catch (MyCustomException e)
{
// Handle the exception
}
Program:
public class EvenNoException extends Exception
{
EvenNoException(String str)
{
super(str); // used to refer the superclass constructor
}
public static void main(String[] args)
{
int arr[]={2,3,4,5};
int rem,i;
for(i=0;i<arr.length;i++)
{
rem=arr[i]%2;
try
{

if(rem==0)
{
System.out.println(arr[i]+" is an Even Number");
}
else
{
EvenNoException exp=new EvenNoException(arr[i]+" is not an EveNumber");
throw exp;
}
}
catch(EvenNoException exp)
{
System.out.println("Exception thrown is "+exp);
}
} // for loop
} // main()
} // class
Output:
C:\example>java EvenNoException
2 is an Even Number
Exception thrown is EvenNoException: 3 is not an EveNumber
4 is an Even Number
Exception thrown is EvenNoException: 5 is not an EveNumber
If the custom exception is a checked exception (it doesn't extend RuntimeException), you need
to either catch it or declare it in the method signature using the throws clause.
By creating user-defined exceptions, you can provide more context-specific error handling and
improve the readability and maintainability of your code. Make sure to choose meaningful
exception names and structures that align with your application's needs.
3.5 MULTITHREADED PROGRAMMING

A thread is a basic execution unit which has its own program counter, set of the register and
stack. But it shares the code, data, and file of the process to which it belongs. A process can
have multiple threads simultaneously, and the CPU switches among these threads so frequently
making an impression on the user that all threads are running simultaneously.
Multithreaded programming in Java involves creating and managing multiple threads of
execution within a single program. Threads are lightweight processes that share the same
memory space, allowing them to run concurrently and perform tasks simultaneously.
Multithreading is used to improve the utilization of CPU cores and to achieve parallelism in
applications.
Figure: Multithreading

Benefits of Multithreading

• Multithreading increases the responsiveness of system as, if one thread of the

application is not responding, the other would respond in that sense the user would not

have to sit idle.

• Multithreading allows resource sharing as threads belonging to the same process can

share code and data of the process and it allows a process to have multiple threads at

the same time active in same address space.

• Creating a different process is costlier as the system has to allocate different memory

and resources to each process, but creating threads is easy as it does not require

allocating separate memory and resources for threads of the same process.

3.5.1 JAVA THREAD MODEL

A Thread is a very light-weighted process, or we can say the smallest part of the process that

allows a program to operate more efficiently by running multiple tasks simultaneously. In order

to perform complicated tasks in the background, we used the Thread concept in Java. All the
tasks are executed without affecting the main program. In a program or process, all the threads
have their own separate path for execution, so each thread of a process is independent.
A thread in Java at any point of time exists in any one of the following states. A thread lies only
in one of the shown states at any instant:
1) New
2) Runnable
3) Blocked
4) Waiting
5) Timed Waiting
6) Terminated
The following figure represents various states of a thread at any instant of time:

Figure: Life Cycle of a thread

1. New Thread:
• When a new thread is created, it is in the new state.
• The thread has not yet started to run when thread is in this state.
• When a thread lies in the new state, its code is yet to be run and hasn’t started to execute.
2. Runnable State:
• A thread that is ready to run is moved to runnable state.
• In this state, a thread might actually be running or it might be ready run at any instant
of time.
• It is the responsibility of the thread scheduler to give the thread, time to run.
• A multi-threaded program allocates a fixed amount of time to each individual thread.
Each and every thread runs for a short while and then pauses and relinquishes the CPU
to another thread, so that other threads can get a chance to run. When this happens, all
such threads that are ready to run, waiting for the CPU and the currently running thread
lies in runnable state.
3. Blocked/Waiting state:
• When a thread is temporarily inactive, then it’s in one of the following states:
➢ Blocked
➢ Waiting
• For example, when a thread is waiting for I/O to complete, it lies in the blocked state.
It’s the responsibility of the thread scheduler to reactivate and schedule a blocked/
waiting thread.
• A thread in this state cannot continue its execution any further until it is moved to
runnable state. Any thread in these states do not consume any CPU cycle.
• A thread is in the blocked state when it tries to access a protected section of code that
is currently locked by some other thread. When the protected section is unlocked, the
schedule picks one of the threads which is blocked for that section and moves it to the
runnable state. A thread is in the waiting state when it waits for another thread on a
condition. When this condition is fulfilled, the scheduler is notified and the waiting
thread is moved to runnable state.
• If a currently running thread is moved to blocked/waiting state, another thread in the
runnable state is scheduled by the thread scheduler to run. It is the responsibility of
thread scheduler to determine which thread to run.
4. Timed Waiting:
• A thread lies in timed waiting state when it calls a method with a time out parameter.
• A thread lies in this state until the timeout is completed or until a notification is received.
• For example, when a thread calls sleep or a conditional wait, it is moved to timed
waiting state.
5. Terminated State:
• A thread terminates because of either of the following reasons:
• Because it exits normally. This happens when the code of thread has entirely executed
by the program.
• Because there occurred some unusual erroneous event, like segmentation fault or an
unhandled exception.
• A thread that lies in terminated state does no longer consumes any cycles of CPU.
3.5.2 CREATING A THREAD AND MULTIPLE THREADS
Creating Threads
• Threading is a facility to allow multiple tasks to run concurrently within a single
process. Threads are independent, concurrent execution through a program, and each
thread has its own stack.
• In Java, there are two ways to create a thread:
i. By extending Thread class.
ii. By implementing Runnable interface.
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
1. public void run(): is used to perform action for a thread.
2. public void start(): starts the execution of the thread. JVM calls the run() method on the
thread.
3. public void sleep(long miliseconds): Causes the currently executing thread to sleep
temporarily cease execution) for the specified number of milliseconds.
4. public void join(): waits for a thread to die.
5. public void join(long miliseconds): waits for a thread to die for the specified
milliseconds.
6. public int getPriority(): returns the priority of the thread.
7. public int setPriority(int priority): changes the priority of the thread.
8. public String getName(): returns the name of the thread.
9. public void setName(String name): changes the name of the thread.
10. public Thread currentThread(): thread.
11. public int getId(): returns the id of the thread.
12. public Thread.State getState(): returns the state of the thread.
13. public boolean isAlive(): tests if the thread is alive.
14. public void yield(): causes the currently executing thread object to temporarily and
allow other threads to execute.
15. public void suspend(): is used to suspend the thread(depricated).
16. public void resume(): is used to resume the suspended thread(depricated).
17. public void stop(): is used to stop the thread(depricated).
18. public boolean isDaemon(): tests if the thread is a daemon thread.
19. public void setDaemon(boolean b): marks the thread as daemon or user thread.
20. public void interrupt(): interrupts the thread.
21. public boolean isInterrupted(): tests if the thread has been interrupted.
22. public static boolean interrupted(): tests if the current thread has been interrupted.
Naming Thread
The Thread class provides methods to change and get the name of a thread. By default, each
thread has a name i.e. thread-0, thread-1 and so on. But we can change the name of the thread
by using setName() method. The syntax of setName() and getName() methods are given below:
public String getName(): is used to return the name of a thread.
public void setName(String name): is used to change the name of a thread.
Extending Thread
The first way to create a thread is to create a new class that extends Thread, and then to create
an instance of that class. The extending class must override the run( ) method, which is the
entry point for the new thread. It must also call start( ) to begin execution of the new thread.
Program:
class ThreadSample extends Thread
{
public void run()
{
try
{
for (int i = 5; i > 0; i--)
{
System.out.println("Child Thread" + i);
Thread.sleep(1000);
}
}
catch (InterruptedException e)
{
System.out.println("Child interrupted");
}
System.out.println("Exiting Child Thread");
}
}
class threadextends
{
public static void main(String[] arg)
{
ThreadSample d = new ThreadSample();
d.start();
try
{

for (int i = 5; i > 0; i--)


{
System.out.println("Main Thread" + i);
Thread.sleep(5000);
}
}
catch (InterruptedException e)
{
System.out.println("Main interrupted");
}
System.out.println("Exiting Main Thread");
}
}
Output:
C:\example>java threadextends
Main Thread5
Child Thread5
Child Thread4
Child Thread3
Child Thread2
Child Thread1
Main Thread4
Exiting Child Thread
Main Thread3
Main Thread2
Main Thread1
Exiting Main Thread
By implementing Runnable interface
Program:
class ThreadSample implements Runnable
{
public void run()
{
try
{
for (int i = 5; i > 0; i--)
{
System.out.println("Child Thread" + i);
Thread.sleep(1000);
}
}
catch (InterruptedException e)
{
System.out.println("Child interrupted");
}

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


}
}
class threadrunnable
{
public static void main(String[] arg)
{
ThreadSample d = new ThreadSample();
Thread s = new Thread(d);
s.start();
try
{
for (int i = 5; i > 0; i--)
{
System.out.println("Main Thread" + i);
Thread.sleep(5000);
}
}
catch (InterruptedException e)
{
System.out.println("Main interrupted");
}
System.out.println("Exiting Main Thread");
}
}
Output:
C:\example>java threadrunnable
Main Thread5
Child Thread5
Child Thread4
Child Thread3
Child Thread2
Child Thread1
Main Thread4
Exiting Child Thread
Main Thread3
Main Thread2
Main Thread1
Exiting Main Thread

3.5.3 PRIORITIES
Each thread has a priority. Priorities are represented by a number between 1 and 10. In most
cases, thread schedular 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.
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.
Syntax:
Thread thread = new Thread(runnable);
thread.setPriority(Thread.NORM_PRIORITY); // Set the thread's priority
Example:
public class MultiPriority extends Thread
{
public void run()
{
System.out.println(“running thread name is:”+Thread.currentThread().getName());
System.out.println(“running thread priority is:”+Thread.currentThread().getPriority());
}
public static void main(String args[])
{
TestMultiPriority1 m1=new TestMultiPriority1();
TestMultiPriority1 m2=new TestMultiPriority1();
m1.setPriority(Thread.MIN_PRIORITY);
m2.setPriority(Thread.MAX_PRIORITY); m1.start();
m2.start(); }}
Output:
running thread name is: Thread-0
running thread priority is:10
running thread name is: Thread-1
running thread priority is:1
3.5.4 SYNCHRONIZATION
• Synchronization in java is the capability to control the access of multiple threads to any
shared resource.
• Java Synchronization is better option where we want to allow only one thread to access
the shared resource.
• When two or more threads need access to a shared resource, 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. Java provides unique, language- level support
for it.
• Key to synchronization is the concept of the monitor (also called a semaphore).
• 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 entered the monitor. All other threads attempting to enter the locked monitor will
be suspended until the first thread exits the monitor.
• These other threads are said to be waiting for the monitor. A thread that owns a monitor
can re-enter the same monitor if it so desires.
Using Synchronized Methods
Synchronization is easy in Java, because all objects have their own implicit monitor associated
with them. To enter an object’s monitor, just call a method that has been modified with the
synchronized keyword. While a thread is inside a synchronized method, all other threads that
try to call it (or any other synchronized method) on the same instance have to wait. To exit the
monitor and relinquish control of the object to the next waiting thread, the owner of the monitor
simply returns from the synchronized method.
Program:
class Table{
synchronized void printTable(int n)//synchronized method
{
for(int i=1;i<=5;i++)
{
System.out.println(n*i);
try
{
Thread.sleep(400);
}
catch(Exception e)
{
System.out.println(e);
}
}
}
}
class MyThread1 extends Thread
{
Table t;
MyThread1(Table t)
{
this.t=t;
}
public void run()
{
t.printTable(5);
}
}
class MyThread2 extends Thread
{
Table t;
MyThread2(Table t)
{
this.t=t;
}
public void run()
{
t.printTable(100);
}
}
public class synchronizationmethod
{
public static void main(String args[])
{
Table obj = new Table(); //only one object
MyThread1 t1=new MyThread1(obj);
MyThread2 t2=new MyThread2(obj);
t1.start();
t2.start();
}
}
Output:
C:\example>java synchronizationmethod
5
10
15
20
25
100
200
300
400
500
Using synchronized Statement
While creating synchronized methods within classes that we create is an easy and effective
means of achieving synchronization, it will not work in all cases. We have to put calls to the
methods defined by the class inside a synchronized block.
Syntax
synchronized(object)
{
// statements to be synchronized
}
Here, object is a reference to the object being synchronized. A synchronized block ensures that
a call to a method that is a member of object occurs only after the current thread has
successfully entered object’s monitor.
Here is an alternative version of the preceding example, using a synchronized block with- in
the run( ) method:
Program:
class Table{
void printTable(int n)
{
synchronized(this) //synchronized block
{
for(int i=1;i<=5;i++){
System.out.println(n*i);
try{ Thread.sleep(400); }catch(Exception e){System.out.println(e);}
}
}
}//end of the method
}

class MyThread1 extends Thread{


Table t;
MyThread1(Table t){
this.t=t;
}
public void run(){
t.printTable(5);
}
}
class MyThread2 extends Thread{
Table t;
MyThread2(Table t){
this.t=t;
}
public void run(){
t.printTable(100);
}
}

public class synchronizationblock


{
public static void main(String args[])
{
Table obj = new Table();//only one object
MyThread1 t1=new MyThread1(obj);
MyThread2 t2=new MyThread2(obj);
t1.start();
t2.start();
}
}
Output:
5
10
15
20
25
100
200
300
400
500

3.5.5 INTER THREAD COMMUNICATION


Inter-thread communication is a technique used in multi-threaded programming to allow
threads to coordinate and communicate with each other in order to synchronize their activities
or exchange data. Threads often need to work together to accomplish a task, and inter-thread
communication provides a way for threads to signal each other, share information, and achieve
synchronization. Interthread communication or co-operation is important when you develop an
application where two or more threads exchange some information. Co-operation is a
mechanism in which a thread is paused running in its critical section and another thread is
allowed to enter (or lock) in the same critical section to be executed. It is implemented by
following methods of Object class:
• wait()
• notify()
• notifyAll()
All these methods belong to object class as final so that all classes have them. They must be
used within a synchronized block only.
1. 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. Syntax:
public final void wait()
2. 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()
3. notifyAll() method
Wakes up all threads that are waiting on this object’s monitor. Syntax:
public final void notifyAll()

Program:

class Customer{
int Balance=10000;

synchronized void withdraw(int amount)


{
System.out.println("going to withdraw..."+amount);

if(Balance<amount)
{
System.out.println("Less balance; Balance = Rs. "+Balance+"\nWaiting for deposit...\n");
try
{
wait();
}
catch(Exception e){}
}
Balance-=amount;
System.out.println("withdraw completed...");
}
synchronized void deposit(int amount)
{
System.out.println("going to deposit... Rs. "+amount);
Balance+=amount;
System.out.println("deposit completed... Balance = "+Balance);
notify();
}
}

class interthread
{
public static void main(String args[]) {
Customer c=new Customer();
new Thread()
{
public void run(){c.withdraw(20000);}
}.start();
new Thread(){
public void run(){c.deposit(15000);}
}.start();
}
}
Output:

C:\example>java interthread
going to withdraw...20000
Less balance; Balance = Rs. 10000
Waiting for deposit...

going to deposit... Rs. 15000


deposit completed... Balance = 25000
withdraw completed...

3.5.6 SUSPENDING AND RESUMING


Suspending threads using the `suspend()` method and resuming them using the `resume()`
method is generally considered unsafe and is not recommended. These methods are part of the
`Thread` class in Java, but they have been deprecated because they can lead to various issues,
including deadlocks and thread contention.
The `suspend()` method can suspend a thread at any point in its execution, even while it holds
critical resources or locks. This can lead to situations where other threads are blocked because
the suspended thread hasn't released the resources it holds. If a thread is suspended while
holding a lock, other threads waiting for the same lock might be blocked indefinitely, causing
deadlocks.
The `resume()` method is used to resume a suspended thread. However, if a thread is resumed
while another thread is waiting for a resource it holds, it can lead to contention and
unpredictable behavior.
Instead of using `suspend()` and `resume()`, it's recommended to use more modern concurrency
mechanisms like the `wait()` and `notify()` methods (or the improved `wait()` and `notifyAll()`
methods), along with the `synchronized` keyword or higher-level concurrency classes like
`ExecutorService`, `ThreadPoolExecutor`, and `java.util.concurrent` classes for managing
threads and synchronization.
If you want to pause and resume threads safely, you should design your code in a way that
threads can gracefully pause their execution based on certain conditions using `wait()` and can
be awakened by other threads using `notify()` or `notifyAll()`.
In summary, avoid using `suspend()` and `resume()` methods and instead focus on using proper
synchronization techniques and concurrency utilities available in Java to achieve safe and
efficient multi-threading.
Program:
import java.util.*;
class mythread extends Thread
{
public void run()
{
try
{
System.out.println("Thread"+Thread.currentThread().getId()+"is running");

Thread.sleep(500);

System.out.println("thread going to sleep for 5 seconds");


Thread.sleep(5000);
System.out.println("thread resumed");

}
catch(Exception e)
{
System.out.println("exception is caught");
}
}
}
class srsthread
{
public static void main(String args[])
{
mythread thread=new mythread();
thread.setName("hello");
thread.start();
thread.suspend();
thread.resume();
}
}
Output:
C:\example>java srsthread
Thread10is running
thread going to sleep for 5 seconds
thread resumed
3.6 WRAPPERS
In Java, wrapper classes are used to convert primitive data types into objects (reference
types) and vice versa. They provide a way to work with primitive data types as objects, which
can be helpful in various situations, especially when dealing with collections, APIs that require
objects, and situations that involve exception handling.
There are eight primitive data types in Java: byte, short, int, long, float, double, char, and
Boolean. For each of these primitive types, there is a corresponding wrapper class:
1. Byte: Wrapper for byte
2. Short: Wrapper for short
3. Integer: Wrapper for int
4. Long: Wrapper for long
5. Float: Wrapper for float
6. Double: Wrapper for double
7. Character: Wrapper for char
8. Boolean: Wrapper for boolean
Wrapper classes provide various methods to manipulate and interact with the underlying
primitive values, as well as to convert between different data types. Some common methods
and features of wrapper classes include:
- Parsing: Converting a string to a primitive value using methods like `parseInt()` and
`parseDouble()`.
- Value extraction: Getting the underlying primitive value using methods like `intValue()`,
`doubleValue()`, etc.
- Static utility methods: Methods like `valueOf()` to convert primitive values into wrapper
objects.
- Constants: For example, `Integer.MAX_VALUE` and `Integer.MIN_VALUE`.
- Autoboxing and Unboxing: The automatic conversion between primitive types and their
corresponding wrapper objects.
Example:
public class WrapperExample {
public static void main(String[] args) {
// Using wrapper classes to work with primitive data types
// Autoboxing: Converting int to Integer
Integer intWrapper = 42;
// Unboxing: Converting Integer to int
int primitiveInt = intWrapper;
// Using wrapper class methods
Double doubleWrapper = Double.valueOf("3.14");
System.out.println("Double Wrapper: " + doubleWrapper);
String numStr = "123"; // Parsing a string to an int
int parsedInt = Integer.parseInt(numStr);
System.out.println("Parsed int: " + parsedInt); // Wrapper class constants
System.out.println("Max valu e of Integer: " + Integer.MAX_VALUE);
System.out.println("Min value of Integer: " + Integer.MIN_VALUE); }}
Output:
Double Wrapper: 3.14
Parsed int: 123
Max value of Integer: 2147483647
Min value of Integer: -2147483648
In this example, you can see how to create wrapper objects, perform autoboxing and
unboxing, use wrapper class methods, and access constants provided by wrapper classes.
Wrapper classes play a significant role in Java programming, helping to bridge the gap between
primitive data types and object-oriented programming concepts.
3.6.1 AUTO-BOXING
Autoboxing is a feature in Java that allows automatic conversion between primitive data types
and their corresponding wrapper classes. It simplifies the process of working with primitive
values and their object representations. Autoboxing automatically converts primitive values to
their wrapper class objects when required, and unboxing does the reverse, converting wrapper
class objects back to primitive values.
Example\
public class AutoboxingExample {
public static void main(String[] args) {
// Autoboxing: Converting int to Integer
int primitiveInt = 42;
Integer wrapperInt = primitiveInt; // Autoboxing
System.out.println("Primitive int: " + primitiveInt);
System.out.println("Wrapper Integer: " + wrapperInt);
// Unboxing: Converting Integer to int
Integer wrapperValue = 123;
int unboxedValue = wrapperValue; // Unboxing
System.out.println("Wrapper Integer: " + wrapperValue);
System.out.println("Unboxed int: " + unboxedValue);
}}
Output:
Primitive int: 42
Wrapper Integer: 42
Wrapper Integer: 123
Unboxed int: 123
In this example:
1. Autoboxing occurs when the `int` value `42` is assigned to the `Integer` variable
`wrapperInt`. The primitive `int` is automatically converted into an `Integer` object. This makes
it convenient to work with collections and methods that expect objects.
2. Unboxing happens when the `Integer` value `123` is assigned to the `int` variable
`unboxedValue`. The `Integer` object is automatically converted back to a primitive `int`.
Autoboxing simplifies code and enhances readability by reducing the need for explicit
conversions between primitive types and wrapper objects. However, it's essential to be aware
of potential performance implications, especially in situations involving frequent autoboxing
and unboxing, as these operations involve object creation and memory overhead.
3.6.2 UNBOXING
Unboxing in Java is the process of converting a wrapper class object to its corresponding
primitive data type. It's the reverse of autoboxing, where a primitive value is converted into
an object of its wrapper class. Unboxing is useful when you need to retrieve the actual
primitive value from a wrapper object.
Example:
public class UnboxingExample {
public static void main(String[] args) {
Integer wrapperInt = 42; // Autoboxing
int primitiveInt = wrapperInt; // Unboxing
System.out.println("Wrapper Integer: " + wrapperInt);
System.out.println("Unboxed int: " + primitiveInt);
Double wrapperDouble = 3.14; // Autoboxing
double primitiveDouble = wrapperDouble; // Unboxing
System.out.println("Wrapper Double: " + wrapperDouble);
System.out.println("Unboxed double: " + primitiveDouble);
}}
Output:
Wrapper Integer: 42
Unboxed int: 42
Wrapper Double: 3.14
Unboxed double: 3.14
In the example:
1. An `Integer` wrapper object named `wrapperInt` is created using autoboxing and assigned
the value `42`.
2. The `wrapperInt` is then assigned to a primitive `int` variable named `primitiveInt`,
resulting in unboxing. This converts the `Integer` object back to a primitive `int`.
3. Similarly, an autoboxed `Double` wrapper object `wrapperDouble` with the value `3.14` is
created.
4. The `wrapperDouble` is then assigned to a primitive `double` variable named
`primitiveDouble`, resulting in unboxing.
Unboxing is automatically handled by the Java compiler, making it easier to work with
wrapper classes when the actual primitive value is needed. Just like autoboxing, it improves
code readability and simplifies conversions between primitive types and their wrapper
objects.

You might also like