Introduction to Exception Handling in Java
🧨 What is Exception Handling in Java?
📘 Definition:
Exception handling in Java is a mechanism that allows a program to detect, handle, and recover from runtime errors or unexpected conditions without crashing.
It ensures that the normal flow of the application is maintained even when errors (called exceptions) occur.
🔶 What is an Exception?
An exception is an event that disrupts the normal flow of a program during execution.
Examples:
- Dividing by zero
- Accessing an array out of bounds
- Trying to open a file that doesn’t exist
- Invalid type casting
📌 Why Exception Handling?
- To prevent the program from crashing unexpectedly
- To provide a graceful way of handling runtime errors
- To help with debugging and error reporting
- To separate error-handling code from normal code
🚦 Java Exception Hierarchy
All exceptions are derived from the class Throwable.
Throwable
/ \
Error Exception
/ \
Checked Unchecked
🔸 Error
- Serious problems like
OutOfMemoryError,StackOverflowError - Not meant to be caught or handled
🔸 Exception
- Problems that can be handled (like
IOException,ArithmeticException) -
Divided into:
-
Checked Exceptions (must be handled)
- Unchecked Exceptions (optional to handle)
✅ Syntax of Exception Handling in Java
Java provides 5 keywords for exception handling:
| Keyword | Description |
|---|---|
try |
Code that might throw an exception |
catch |
Block that handles the exception |
finally |
Block that always executes (used for cleanup) |
throw |
Used to manually throw an exception |
throws |
Declares the exceptions a method might throw |
📘 Basic Example:
public class Example {
public static void main(String[] args) {
try {
int result = 10 / 0; // This will throw ArithmeticException
} catch (ArithmeticException e) {
System.out.println("Cannot divide by zero!");
} finally {
System.out.println("Finally block always executes.");
}
}
}
🔍 Types of Exceptions
| Type | Examples | Must Handle? |
|---|---|---|
| Checked Exception | IOException, SQLException | ✅ Yes |
| Unchecked Exception | ArithmeticException, NullPointerException | ❌ No (but recommended) |
💡 Real-life Analogy
Imagine you're withdrawing money from an ATM:
- Normal flow: Enter card → enter PIN → withdraw cash
- Exception: No money in account → Exception occurs
- Handler: Show message "Insufficient funds" (instead of crashing the ATM!)
📝 Common Exam Questions
- What is exception handling in Java?
- Differentiate between checked and unchecked exceptions.
- Explain the try-catch-finally block with an example.
- What is the difference between
throwandthrows?
✅ Summary
- Exception handling = catching and managing runtime errors
- Use
try,catch,finally,throw, andthrows - Prevents program from crashing unexpectedly
- Helps in debugging and writing reliable code
Fundamentals of Exception Handling in Java.
Understanding these fundamentals is essential for writing safe, predictable, and professional-level Java code.
📘 What is Exception Handling?
Exception Handling is a mechanism to:
- Detect errors during program execution (runtime errors),
- Handle those errors in a controlled way,
- Allow the program to recover or fail gracefully.
⚙️ Fundamental Concepts of Exception Handling
🔹 1. Exception
An exception is an abnormal condition or event that occurs during the execution of a program and disrupts the normal flow of instructions.
🔹 2. Types of Exceptions
| Type | Description | Examples |
|---|---|---|
| Checked Exception | Checked at compile time | IOException, SQLException |
| Unchecked Exception | Checked at runtime | ArithmeticException, NullPointerException |
| Errors | Serious problems that applications should not try to handle | OutOfMemoryError, StackOverflowError |
🔹 3. Java Exception Class Hierarchy
Throwable
/ \
Error Exception
/ \
Checked Unchecked
- All exceptions are subclasses of Throwable
- Error: JVM-related serious problems (not catchable)
- Exception: Represents application-level problems (can be handled)
🔹 4. Keywords in Exception Handling
| Keyword | Purpose |
|---|---|
try |
Defines a block of code to test for errors |
catch |
Handles the exception if it occurs |
finally |
Block that always executes, used for cleanup |
throw |
Used to throw an exception manually |
throws |
Declares that a method may throw an exception |
🔹 5. Basic Syntax
try {
// code that may throw exception
} catch (ExceptionType e) {
// code to handle the exception
} finally {
// code that always runs (optional)
}
🧪 Example:
public class Example {
public static void main(String[] args) {
try {
int result = 5 / 0;
} catch (ArithmeticException e) {
System.out.println("Can't divide by zero!");
} finally {
System.out.println("This will always execute.");
}
}
}
🔹 6. Multiple Catch Blocks
try {
// code
} catch (IOException e) {
// handles IO exception
} catch (ArithmeticException e) {
// handles arithmetic error
}
✅ Catch more specific exceptions before general ones
❌ Do NOT place Exception before more specific ones—it will cause a compile error.
🔹 7. Nested try blocks
Java allows try blocks inside try blocks.
try {
try {
// inner try
} catch (...) {}
} catch (...) {}
🔹 8. Custom Exception Classes
You can create your own exceptions by extending the Exception class.
class MyException extends Exception {
public MyException(String message) {
super(message);
}
}
✅ Summary
| Concept | Description |
|---|---|
| Exception | A runtime error or abnormal condition |
| try-catch-finally | Used to handle exceptions |
| throw | Used to throw an exception manually |
| throws | Declares an exception from a method |
| Checked Exception | Must be handled or declared |
| Unchecked Exception | Optional to handle (runtime errors) |
📚 Example Questions for Exams
- Define exception and explain types of exceptions.
- Explain the working of try-catch-finally block.
- Differentiate between
throwandthrows. - What is the importance of exception handling?
throw and throws keywords in Java.
🔹 1. What is throw in Java?
📘 Definition:
The **throw** keyword is used to manually throw an exception in Java.
You use
throwwhen you want to create and throw an exception yourself.
🔧 Syntax:
throw new ExceptionType("Error Message");
🧪 Example:
public class TestThrow {
public static void main(String[] args) {
int age = 15;
if (age < 18) {
throw new ArithmeticException("Access denied - You must be 18 or older.");
}
System.out.println("Access granted.");
}
}
🔍 Key Points:
- Only one exception can be thrown at a time using
throw. - After
throw, the program stops executing unless the exception is caught. - The object thrown must be an instance of a subclass of
Throwable.
🔹 2. What is throws in Java?
📘 Definition:
The **throws** keyword is used in a method declaration to specify that the method might throw one or more exceptions.
You use
throwsto inform the caller that it needs to handle or declare the exception.
🔧 Syntax:
returnType methodName() throws ExceptionType1, ExceptionType2 {
// method body
}
🧪 Example:
import java.io.*;
public class TestThrows {
public static void readFile() throws IOException {
FileReader file = new FileReader("data.txt"); // Might throw IOException
file.read();
file.close();
}
public static void main(String[] args) {
try {
readFile(); // You must handle the exception here
} catch (IOException e) {
System.out.println("File not found or error reading file.");
}
}
}
🔍 Key Points:
- Used for checked exceptions only (like
IOException,SQLException) - Tells the caller of the method: “Be ready to handle this exception.”
🔁 Difference Between throw and throws
| Feature | throw |
throws |
|---|---|---|
| Purpose | To actually throw an exception | To declare possible exceptions |
| Placement | Inside method body | In method declaration |
| Number of exceptions | Only one can be thrown at a time | Multiple exceptions can be declared |
| Usage | Followed by exception object | Followed by exception class names |
| Example | throw new IOException(); |
throws IOException, SQLException |
📝 Common Exam Questions
- Differentiate between
throwandthrowsin Java. - Write a program that demonstrates the use of
throw. - Write a method that uses
throwsto declare multiple exceptions.
✅ Summary
- ✅ Use
throwto manually generate an exception. - ✅ Use
throwsto declare exceptions a method might throw. - Both help in robust error handling and clean exception reporting.
try and catch
- the two core components of Java’s exception handling mechanism.
🔹 What is try in Java?
📘 Definition:
The try block contains the code that might throw an exception. You put the risky or error-prone code inside this block.
Purpose:
- To test a block of code for exceptions.
- If an exception occurs inside
try, control immediately jumps to the appropriatecatchblock.
Syntax:
try {
// code that might throw an exception
}
🔹 What is catch in Java?
📘 Definition:
The catch block handles the exception that occurs inside the try block.
Purpose:
- To catch and handle exceptions so the program can continue or terminate gracefully.
- It receives an exception object that provides information about the error.
Syntax:
catch (ExceptionType e) {
// code to handle the exception
}
🔹 How do try and catch work together?
try {
// risky code
} catch (ExceptionType e) {
// handler code
}
- The program tries to execute the code in
try. - If no exception occurs,
catchis skipped. - If an exception of type
ExceptionTypeoccurs, control moves to the matchingcatchblock. - If exception is not caught, it propagates up and may crash the program.
🧪 Example:
public class TryCatchExample {
public static void main(String[] args) {
try {
int division = 10 / 0; // Causes ArithmeticException
System.out.println(division);
} catch (ArithmeticException e) {
System.out.println("Error: Cannot divide by zero!");
}
}
}
Output:
Error: Cannot divide by zero!
🔹 Multiple Catch Blocks
You can handle different exceptions differently by using multiple catch blocks:
try {
// code
} catch (ArithmeticException e) {
// handle divide by zero
} catch (NullPointerException e) {
// handle null pointer
}
🔹 Important Notes
- You must catch checked exceptions or declare them with
throws. - The
catchblock only catches exceptions that match its parameter type or subclasses. trymust be followed by at least onecatchblock or afinallyblock.
📝 Common Exam Questions
- What is the purpose of the
tryblock in exception handling? - How does the
catchblock work? Give an example. - Can there be multiple
catchblocks? Explain with an example.
✅ Summary
| Keyword | Purpose |
|---|---|
try |
Contains code that may throw exceptions |
catch |
Handles the thrown exceptions |
Threads in Java
- a fundamental concept for writing programs that can do multiple things at the same time (concurrency).
🔹 What is a Thread?
📘 Definition:
A thread is the smallest unit of execution within a process. It is like a lightweight subprocess.
- A process can have multiple threads running concurrently.
- Threads share the same memory space but execute independently.
Real-life analogy:
Think of a process as a restaurant kitchen, and threads are the chefs who work simultaneously on different tasks (cutting veggies, cooking, plating).
🔹 Why Use Threads?
- To perform multiple tasks simultaneously (parallelism).
- To improve the performance of applications.
- Useful in GUI apps (so UI doesn’t freeze), servers (handle multiple clients), games, etc.
🔹 Thread Lifecycle in Java
A thread can be in one of these states:
| State | Description |
|---|---|
| New | Thread object created but not started yet. |
| Runnable | Ready to run and waiting for CPU time. |
| Running | Thread is executing. |
| Blocked/Waiting | Waiting for a resource or signal. |
| Terminated | Thread has finished execution or stopped. |
🔹 How to Create Threads in Java?
1. By Extending Thread Class
class MyThread extends Thread {
public void run() {
System.out.println("Thread running");
}
}
public class Main {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start(); // Starts the new thread and calls run()
}
}
2. By Implementing Runnable Interface
class MyRunnable implements Runnable {
public void run() {
System.out.println("Runnable running");
}
}
public class Main {
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable());
t1.start();
}
}
🔹 Difference between start() and run()
| Method | Description |
|---|---|
start() |
Creates a new thread and calls run() internally |
run() |
Just executes the code on current thread like a normal method call |
🔹 Thread Methods
start()– start thread executionrun()– thread's entry pointsleep(milliseconds)– pauses thread for a given timejoin()– waits for thread to finishyield()– temporarily pause to allow other threads to execute
🔹 Example:
class MyThread extends Thread {
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println("Thread: " + i);
try {
Thread.sleep(500); // Pause for 0.5 seconds
} catch (InterruptedException e) {
System.out.println("Thread interrupted");
}
}
}
}
public class TestThread {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
for (int i = 1; i <= 5; i++) {
System.out.println("Main: " + i);
}
}
}
🔹 Benefits of Threads
- Efficient use of CPU
- Better application responsiveness
- Allows concurrent operations like file reading while UI remains responsive
📝 Common Exam Questions
- What is a thread in Java?
- Explain two ways to create a thread.
- What is the difference between
start()andrun()? - Describe the lifecycle of a thread.
✅ Summary
- A thread is a lightweight process — multiple threads run inside a process.
- Two ways to create threads: extend
Threador implementRunnable. - Use
start()to begin a new thread. - Threads help in performing multiple tasks concurrently.
Synchronization in Java
🔹 What is Synchronization?
📘 Definition:
Synchronization in Java is a mechanism to control access to shared resources by multiple threads to avoid conflicts and inconsistent data.
🔹 Why Synchronization is Needed?
When multiple threads try to access and modify shared data at the same time, it can cause problems like:
- Race conditions: When two threads change a variable simultaneously leading to incorrect results.
- Data inconsistency: Corrupted or unexpected data due to unsynchronized access.
🔹 How Synchronization Works?
- Java provides the
synchronizedkeyword. - When a method or block is marked as synchronized, only one thread can access it at a time.
- Other threads trying to access the synchronized code will wait (blocked) until the lock is released.
🔹 Synchronization on Methods
public synchronized void method() {
// critical section code (shared resource)
}
- The thread must acquire the lock on the object before entering.
- Once inside, no other thread can enter any synchronized method on the same object.
🔹 Synchronization on Blocks
public void method() {
synchronized(this) {
// critical section code
}
}
- Synchronizes only a part of the code, improving performance by limiting locked code.
🔹 Locks in Synchronization
- Every object in Java has a lock (monitor).
- Synchronization is about acquiring and releasing the object's lock.
- Only the thread holding the lock can execute synchronized code on that object.
🧪 Example:
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
}
public class TestSync {
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Count: " + counter.getCount()); // Should print 2000
}
}
Without synchronization, the count value may be incorrect due to race conditions.
🔹 Important Points
- Synchronization can reduce performance because threads wait to acquire locks.
- Use synchronization only when necessary to protect shared mutable data.
- Use
volatilekeyword when you want visibility but not atomicity.
📝 Common Exam Questions
- What is synchronization in Java and why is it necessary?
- How does the
synchronizedkeyword work? - What is the difference between synchronized methods and synchronized blocks?
- Explain race conditions and how synchronization solves it.
✅ Summary
| Concept | Description |
|---|---|
| Synchronization | Ensures only one thread accesses critical code at a time |
synchronized |
Keyword to lock methods or blocks on an object |
| Lock/Monitor | Every object has a lock used for synchronization |
| Race condition | When threads interfere causing inconsistent data |
messaging
🔹 What is Messaging?
📘 Definition:
Messaging is the process of sending and receiving data or information between two or more entities (such as programs, components, or systems), usually asynchronously.
In the context of software and distributed systems, messaging allows different parts of an application or different applications to communicate and coordinate without being directly connected all the time.
🔹 Why is Messaging Important?
- Enables communication between different software components or services.
- Supports asynchronous processing — sender and receiver don’t need to interact at the same time.
- Helps build loosely coupled systems, improving scalability and reliability.
- Commonly used in distributed systems, microservices, client-server models, etc.
🔹 Types of Messaging
| Type | Description | Example/Use Case |
|---|---|---|
| Synchronous | Sender waits for receiver to process and respond immediately. | HTTP request-response |
| Asynchronous | Sender sends message and continues; receiver processes it later. | Message queues like RabbitMQ |
| One-way | Message sent without expecting a response. | Event notifications |
| Two-way | Sender expects a reply (request-response pattern). | Remote Procedure Calls (RPC) |
🔹 Messaging Models
- Point-to-Point (Queue): Messages sent to a queue; one receiver consumes a message.
- Publish-Subscribe (Topic): Messages published to a topic; multiple subscribers receive messages.
🔹 Messaging in Java Context
In Java, messaging often refers to Java Messaging Service (JMS):
- JMS is a Java API that allows applications to create, send, receive, and read messages.
- Supports asynchronous communication between distributed components.
- Two main messaging models: Queue (Point-to-Point) and Topic (Publish-Subscribe).
🧪 Simple analogy:
Imagine a post office system:
- You drop a letter (message) into a mailbox (queue).
- The recipient collects the letter when available.
- You don’t have to wait in person; this is asynchronous messaging.
📝 Common Exam Questions
- Define messaging in the context of distributed systems.
- Differentiate synchronous and asynchronous messaging.
- What is the point-to-point messaging model?
- Explain the publish-subscribe messaging model.
✅ Summary
| Aspect | Explanation |
|---|---|
| Messaging | Sending and receiving data/information |
| Sync vs Async | Sync: waits for reply; Async: continues immediately |
| Models | Point-to-point (queue), publish-subscribe (topic) |
| Use in Java | Java Messaging Service (JMS) API |
Runnable interface in Java.
🔹 What is the Runnable Interface?
📘 Definition:
Runnable is a functional interface in Java that represents a task that can be executed by a thread. It only has one method, run(), which contains the code that defines the task.
🔹 Purpose of Runnable
- It is used to define a unit of work or task that can be run in a thread.
- Allows you to separate the task from the Thread object.
- Provides more flexibility, especially when your class needs to extend some other class (since Java supports only single inheritance).
🔹 The Runnable Interface Method
public interface Runnable {
public abstract void run();
}
- You implement this interface and provide the body for the
run()method. - The
run()method is where you put the code that should execute in a separate thread.
🔹 How to Use Runnable?
Step 1: Implement Runnable in a class
class MyRunnable implements Runnable {
public void run() {
System.out.println("Thread is running");
}
}
Step 2: Create a Thread object passing the Runnable instance
public class TestRunnable {
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable); // Pass Runnable to Thread
thread.start(); // Start the new thread
}
}
🔹 Why Use Runnable Instead of Extending Thread?
| Runnable Interface | Extending Thread Class |
|---|---|
| Allows your class to extend from another class | Limits you because Java supports single inheritance |
| More flexible to reuse the task in multiple threads | Thread object and task are combined |
| Cleaner separation of task and thread | Less flexible |
🧪 Example:
class MyTask implements Runnable {
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println("Runnable Thread: " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
System.out.println("Thread interrupted");
}
}
}
}
public class RunnableDemo {
public static void main(String[] args) {
Thread thread = new Thread(new MyTask());
thread.start();
for (int i = 1; i <= 5; i++) {
System.out.println("Main Thread: " + i);
}
}
}
📝 Common Exam Questions
- What is the Runnable interface in Java?
- How is Runnable different from extending the Thread class?
- Write a program using Runnable to create a thread.
✅ Summary
Runnableis a functional interface with the single methodrun().- Implementing
Runnablelets you define a task that a thread will execute. - It provides better design flexibility than extending
Thread. - You create a thread by passing a
Runnableinstance to aThreadconstructor.
Thread class in Java.
🔹 What is the Thread Class?
📘 Definition:
The Thread class in Java represents a thread of execution — a lightweight process that can run concurrently with other threads.
- It’s part of
java.langpackage. - You use the
Threadclass to create and control threads.
🔹 How Threads Work in Java?
- A Java program runs in at least one thread (the main thread).
- You can create additional threads for multitasking.
- Each thread runs independently but shares the same memory within the process.
🔹 Ways to Create a Thread Using Thread Class
1. Extend the Thread class and override its run() method.
class MyThread extends Thread {
public void run() {
System.out.println("Thread running");
}
}
public class TestThread {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start(); // Starts the thread and calls run()
}
}
2. Alternatively, you can pass a Runnable object to a Thread constructor (we covered this before).
🔹 Important Methods in Thread Class
| Method | Description |
|---|---|
start() |
Starts the thread and calls the run() method in a new thread. |
run() |
Contains the code executed by the thread. |
sleep(long millis) |
Pauses the thread for specified milliseconds. |
join() |
Waits for a thread to finish before continuing. |
setPriority(int p) |
Sets the priority of a thread (1 to 10). |
getName() |
Returns the thread’s name. |
setName(String name) |
Sets the thread’s name. |
interrupt() |
Interrupts a thread (used to stop waiting or sleeping threads). |
isAlive() |
Checks if the thread is still running. |
🔹 start() vs run() Methods
start() |
run() |
|---|---|
Creates a new thread and calls run() internally |
Just executes run() method on the current thread (like a normal method call) |
| Should be called to start the thread execution | Should not be called directly to create a new thread |
🧪 Example:
class MyThread extends Thread {
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println("Thread: " + i);
try {
Thread.sleep(500); // Pause for 0.5 seconds
} catch (InterruptedException e) {
System.out.println("Thread interrupted");
}
}
}
}
public class TestThreadClass {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.setName("MyThread-1");
t1.start();
System.out.println("Main thread finished");
}
}
🔹 Thread Life Cycle States Recap
- New: Thread object created but not started.
- Runnable: Ready to run but waiting for CPU.
- Running: Thread executing.
- Blocked/Waiting: Waiting for resource or signal.
- Terminated: Thread finished or stopped.
📝 Common Exam Questions
- How do you create a thread by extending the Thread class?
- What is the difference between
start()andrun()methods? - Name some important methods of the Thread class.
- Explain the thread life cycle briefly.
✅ Summary
| Concept | Description |
|---|---|
| Thread class | Represents a thread of execution in Java |
| Create thread | Extend Thread and override run(), then call start() |
| Key methods | start(), run(), sleep(), join(), interrupt() |
start() vs run() |
start() creates new thread; run() executes in current thread |
multiple threads in Java.
🔹 Creating Multiple Threads in Java
You can create multiple threads by either:
- Extending the
Threadclass multiple times, or - Implementing the
Runnableinterface multiple times.
1. Using Thread Class (Extending Thread)
class MyThread extends Thread {
private String threadName;
MyThread(String name) {
threadName = name;
}
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println(threadName + " is running: " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
System.out.println(threadName + " interrupted.");
}
}
System.out.println(threadName + " finished.");
}
}
public class MultiThreadExample {
public static void main(String[] args) {
MyThread t1 = new MyThread("Thread-1");
MyThread t2 = new MyThread("Thread-2");
MyThread t3 = new MyThread("Thread-3");
t1.start();
t2.start();
t3.start();
}
}
2. Using Runnable Interface (Recommended way)
class MyRunnable implements Runnable {
private String threadName;
MyRunnable(String name) {
threadName = name;
}
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println(threadName + " is running: " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
System.out.println(threadName + " interrupted.");
}
}
System.out.println(threadName + " finished.");
}
}
public class MultiRunnableExample {
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable("Runnable-1"));
Thread t2 = new Thread(new MyRunnable("Runnable-2"));
Thread t3 = new Thread(new MyRunnable("Runnable-3"));
t1.start();
t2.start();
t3.start();
}
}
🔹 What Happens When You Run This?
- Each thread runs independently and concurrently.
- Output will be interleaved because threads run in parallel.
- Threads share the same CPU but switch rapidly.
🔹 Notes
- Use
Thread.sleep()to simulate work or pause. - You can use
join()to wait for a thread to finish if needed. - Creating many threads may impact performance, so be cautious.
📝 Common Exam Questions
- How do you create multiple threads in Java?
- Explain the difference between extending
Threadand implementingRunnablefor multiple threads. - What happens when you call
start()on multiple thread objects?
✅ Summary
| Method | Description |
|---|---|
| Extend Thread | Create multiple subclasses and start each |
| Implement Runnable | Create multiple Runnable objects, pass to Thread, and start |
| Thread.sleep() | Pause thread execution to simulate work |
| Threads run concurrently | Outputs from threads interleave |
Interthread Communication
🔹 What is Interthread Communication?
📘 Definition:
Interthread Communication refers to the process where two or more threads communicate with each other to coordinate their actions, especially when sharing resources.
This is crucial to avoid problems like:
- Race conditions
- Deadlocks
- Data inconsistency
🔹 Why is Interthread Communication Needed?
- Sometimes one thread must wait for another thread to complete or produce data.
- Threads need to send signals or notifications to each other about changes in state.
- It helps in synchronizing tasks that depend on each other.
🔹 How Does Java Support Interthread Communication?
Java provides three key methods inside Object class used for interthread communication:
| Method | Description |
|---|---|
wait() |
Makes the current thread wait until another thread calls notify() or notifyAll() on the same object. The thread releases the lock and waits. |
notify() |
Wakes up one thread waiting on the object's monitor. If multiple threads wait, one is chosen arbitrarily. |
notifyAll() |
Wakes up all threads waiting on the object's monitor. |
🔹 Important Points
wait(),notify(), andnotifyAll()must be called within a synchronized block or method to hold the object's lock.- When a thread calls
wait(), it releases the lock and goes to the waiting state. - When notified, the thread moves to the runnable state but must re-acquire the lock before continuing.
🧪 Example: Producer-Consumer Problem
class Data {
private int number;
private boolean available = false;
public synchronized void produce(int num) throws InterruptedException {
while (available) {
wait(); // Wait if data is available (consumer has not consumed yet)
}
this.number = num;
System.out.println("Produced: " + num);
available = true;
notify(); // Notify consumer
}
public synchronized void consume() throws InterruptedException {
while (!available) {
wait(); // Wait if data is not yet produced
}
System.out.println("Consumed: " + number);
available = false;
notify(); // Notify producer
}
}
class Producer implements Runnable {
Data data;
Producer(Data data) {
this.data = data;
}
public void run() {
for (int i = 1; i <= 5; i++) {
try {
data.produce(i);
Thread.sleep(500);
} catch (InterruptedException e) { }
}
}
}
class Consumer implements Runnable {
Data data;
Consumer(Data data) {
this.data = data;
}
public void run() {
for (int i = 1; i <= 5; i++) {
try {
data.consume();
Thread.sleep(500);
} catch (InterruptedException e) { }
}
}
}
public class InterThreadCommExample {
public static void main(String[] args) {
Data data = new Data();
Thread producer = new Thread(new Producer(data));
Thread consumer = new Thread(new Consumer(data));
producer.start();
consumer.start();
}
}
🔹 Explanation of the Example
- The producer produces data and waits if the consumer hasn’t consumed it yet.
- The consumer consumes data and waits if there is no data available.
wait()causes threads to wait and release the lock.notify()wakes up waiting threads to continue.
📝 Common Exam Questions
- What is interthread communication?
- Explain the use of
wait(),notify(), andnotifyAll(). - Why must
wait(),notify(), andnotifyAll()be called within synchronized blocks? - Describe the producer-consumer problem and how interthread communication solves it.
✅ Summary
| Method | Purpose |
|---|---|
wait() |
Pause thread and release lock until notified |
notify() |
Wake up one waiting thread |
notifyAll() |
Wake up all waiting threads |
| Synchronization | Must use synchronized block or method |
Deadlock in Java multithreading.
🔹 What is Deadlock?
📘 Definition:
A deadlock is a situation in multithreading where two or more threads are blocked forever, each waiting for a resource held by the other, creating a cycle of dependency with no thread able to proceed.
🔹 How Does Deadlock Occur?
Deadlock happens when all the following conditions are true simultaneously (known as Coffman conditions):
- Mutual Exclusion: At least one resource is held in a non-shareable mode (only one thread can use it at a time).
- Hold and Wait: A thread holding at least one resource is waiting to acquire additional resources held by other threads.
- No Preemption: Resources cannot be forcibly taken away from threads holding them.
- Circular Wait: A closed chain of threads exists, where each thread holds a resource the next thread needs.
🔹 Visualizing Deadlock
Imagine:
- Thread A holds Resource 1 and waits for Resource 2.
- Thread B holds Resource 2 and waits for Resource 1.
Neither can proceed — they’re stuck forever, causing a deadlock.
🔹 Deadlock in Java Example
public class DeadlockDemo {
public static void main(String[] args) {
final Object resource1 = "Resource 1";
final Object resource2 = "Resource 2";
// Thread 1 tries to lock resource1 then resource2
Thread t1 = new Thread(() -> {
synchronized(resource1) {
System.out.println("Thread 1 locked resource 1");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized(resource2) {
System.out.println("Thread 1 locked resource 2");
}
}
});
// Thread 2 tries to lock resource2 then resource1
Thread t2 = new Thread(() -> {
synchronized(resource2) {
System.out.println("Thread 2 locked resource 2");
try { Thread.sleep(100); } catch (InterruptedException e) {}
synchronized(resource1) {
System.out.println("Thread 2 locked resource 1");
}
}
});
t1.start();
t2.start();
}
}
What happens here?
- Thread 1 locks resource1 and waits for resource2.
- Thread 2 locks resource2 and waits for resource1.
- Both threads wait forever → deadlock.
🔹 How to Avoid Deadlock?
1. Avoid Nested Locks
Reduce the scope of synchronized blocks and avoid locking multiple resources simultaneously.
2. Lock Ordering
Always acquire locks in the same order in all threads.
Example: Both threads should lock resource1 then resource2.
3. Use tryLock() (with ReentrantLock)
Try to acquire locks with timeout, so you can back off if unable to get the lock.
4. Avoid Hold and Wait
Ensure threads acquire all required resources at once or release held resources before waiting.
5. Deadlock Detection Tools
Use thread dumps and debugging tools to detect deadlocks during testing.
🔹 Detecting Deadlock
- In Java, you can use
jstackto get thread dumps and look for deadlock patterns. - Tools like VisualVM, JConsole can help visualize deadlocks.
📝 Common Exam Questions
- Define deadlock in Java.
- List the four conditions required for deadlock.
- Write a simple Java program demonstrating deadlock.
- How can deadlock be prevented?
✅ Summary
| Aspect | Description |
|---|---|
| Deadlock | Threads waiting forever for each other’s resources |
| Conditions | Mutual exclusion, hold & wait, no preemption, circular wait |
| Avoidance | Lock ordering, avoid nested locks, use tryLock, avoid hold & wait |
| Detection | Thread dumps, monitoring tools |
suspending, resuming, and stopping
🔹 Suspending, Resuming, and Stopping Threads in Java
Background:
Java provides some methods in the Thread class for suspending, resuming, and stopping threads:
suspend()resume()stop()
However, these methods are deprecated and unsafe! Using them can lead to deadlocks and inconsistent thread states.
1. Thread.suspend()
- Temporarily pauses the execution of a thread.
- The thread stays suspended until resumed.
- Deprecated because suspending a thread holding locks can cause other threads to deadlock waiting for those locks.
2. Thread.resume()
- Resumes a thread that was suspended by
suspend(). - Deprecated for the same reasons.
3. Thread.stop()
- Immediately terminates a thread.
- Deprecated because it can stop a thread in the middle of execution, potentially leaving shared resources in an inconsistent state (like partially updated variables).
🔹 Why Are These Methods Deprecated?
- Deadlock risks:
suspend()can pause a thread while it holds a lock, blocking other threads indefinitely. - Inconsistent state:
stop()terminates a thread abruptly, without proper cleanup. - Unsafe resource management: Locks or resources may never be released.
🔹 Recommended Alternatives
Java encourages safer ways to manage thread lifecycle using flags or interrupts:
Using a flag to pause/resume:
class ControlledThread extends Thread {
private volatile boolean suspended = false;
public void run() {
while (true) {
synchronized(this) {
while (suspended) {
try {
wait(); // wait until notified to resume
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
// Perform thread work here
System.out.println("Thread running...");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
public synchronized void suspendThread() {
suspended = true;
}
public synchronized void resumeThread() {
suspended = false;
notify();
}
}
Using interrupt() to stop:
class InterruptibleThread extends Thread {
public void run() {
try {
while (!Thread.currentThread().isInterrupted()) {
// Thread work here
System.out.println("Thread running...");
Thread.sleep(500);
}
} catch (InterruptedException e) {
System.out.println("Thread interrupted and stopping.");
Thread.currentThread().interrupt(); // preserve interrupt status
}
}
}
public class ThreadControlExample {
public static void main(String[] args) throws InterruptedException {
ControlledThread t = new ControlledThread();
t.start();
Thread.sleep(2000);
t.suspendThread();
System.out.println("Thread suspended");
Thread.sleep(2000);
t.resumeThread();
System.out.println("Thread resumed");
Thread.sleep(2000);
t.interrupt(); // Using interrupt to stop
System.out.println("Thread interrupted for stopping");
}
}
📝 Key Points for Exams
| Method | Status | What it does | Why deprecated / problems |
|---|---|---|---|
suspend() |
Deprecated | Pauses thread execution | Can cause deadlocks if thread holds locks |
resume() |
Deprecated | Resumes suspended thread | Same as suspend |
stop() |
Deprecated | Stops thread immediately | Unsafe, may corrupt shared data |
wait()/notify() |
Recommended | Use in custom flags for safe pause/resume | Safer thread coordination |
interrupt() |
Recommended | Used to signal thread to stop or wake up | Cooperative way to stop a thread safely |
Summary
- Avoid
suspend(),resume(),stop()methods in Java. - Use flags + wait/notify for pause/resume behavior.
- Use interrupt() method for safely stopping threads.
- Proper synchronization and handling of interrupts is essential for safe thread control.
Multithreading
🔹 What is Multithreading?
📘 Definition:
Multithreading is a Java feature that allows a program to run multiple threads simultaneously within a single process.
- A thread is the smallest unit of execution.
- Multithreading enables concurrent execution of two or more parts of a program for maximum CPU utilization.
🔹 Why Use Multithreading?
- To perform multiple tasks at the same time (e.g., loading data, user interface, background calculations).
- To improve application performance on multi-core processors.
- To enhance resource sharing among threads.
- To create responsive programs where one thread can run in the background without blocking others.
🔹 Key Concepts
| Term | Description |
|---|---|
| Process | Program in execution, with its own memory space. |
| Thread | Lightweight sub-process, shares process memory. |
| Main Thread | The initial thread created by JVM to run main() method. |
| Context Switching | CPU switching between threads, enabling multitasking. |
🔹 How to Create Threads in Java?
1. Extending the Thread Class
class MyThread extends Thread {
public void run() {
System.out.println("Thread running: " + Thread.currentThread().getName());
}
}
public class Test {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start(); // starts new thread and calls run()
}
}
2. Implementing the Runnable Interface (Preferred)
class MyRunnable implements Runnable {
public void run() {
System.out.println("Thread running: " + Thread.currentThread().getName());
}
}
public class Test {
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable());
t1.start();
}
}
🔹 Thread Lifecycle
| State | Description |
|---|---|
| New | Thread created but not started yet |
| Runnable | Ready to run and waiting for CPU |
| Running | Thread is executing |
| Waiting/Blocked | Waiting for a resource or event |
| Terminated | Thread finished or stopped |
🔹 Advantages of Multithreading
- Better resource utilization: Multiple threads share process resources efficiently.
- Responsiveness: User interfaces stay responsive while background tasks run.
- Faster execution: Parallelism on multi-core CPUs.
- Simplifies program structure: For some problems like servers or real-time apps.
🔹 Challenges of Multithreading
- Synchronization issues: Data inconsistency due to concurrent access.
- Deadlocks: Threads waiting indefinitely for each other.
- Race conditions: Unpredictable results due to timing issues.
- Thread management overhead: Context switching costs.
📝 Common Exam Questions
- What is multithreading?
- How do you create a thread in Java?
- What are the states in the thread lifecycle?
- What are the benefits and challenges of multithreading?
✅ Summary
| Topic | Key Points |
|---|---|
| Multithreading | Running multiple threads simultaneously |
| Creating Threads | Extend Thread or implement Runnable |
| Thread Lifecycle | New → Runnable → Running → Waiting → Terminated |
| Benefits | Better CPU utilization, responsiveness |
| Challenges | Synchronization, deadlocks, race conditions |