Introduction to Threads in Java
• A thread is the smallest unit of a process that can run
independently.
• In Java, multithreading allows a program to perform multiple
tasks simultaneously.
• Every Java program has at least one thread – the main thread
(created automatically when the program starts).
• All Java programs have at least one thread, known as main
thread, which is created by the JVM at the program’s start, when
the main() method is invoked with main thread.
• Every Java thread is created and controlled by the
[Link] class.
Why Threads?
To perform multiple tasks at the same time.
To utilize CPU efficiently.
To make programs responsive (example: downloading a file
while playing music).
Key Features of Threads in Java
1. Lightweight – share the same memory of the process but
run independently.
2. Multitasking – multiple threads can run concurrently.
3. Controlled execution – threads can be paused, resumed, or
stopped.
Types of Threads in Java
1. Daemon Threads
2. User Threads
Daemon Threads
• Daemon threads are threads which are mostly created by the
JVM.
• These threads always run in background.
• These threads are used to perform some background tasks like
garbage collection.
• These threads are less priority threads or background threads.
• JVM will not wait for these threads to finish their execution.
• JVM will exit as soon as all user threads finish their execution.
User Threads
• User threads are threads which are created by the
application or user.
• They are high priority threads.
• JVM (Java Virtual Machine) will not exit until all user threads
finish their execution.
• JVM wait for these threads to finish their task.
There are two types of user threads:
1. Single-threaded program
2. Multithreaded program
Single-threaded program
• A thread is similar to a program that has a body, and an end, and
executes commands sequentially.
• In fact, all main programs in our previous examples can be called
single-threaded programs.
Multithreaded program
• Multithreading refers two or more tasks executing concurrently
within a single program.
• A thread is an independent path of execution within a program Java
provides built-in support for multithreaded programming.
Advantages of Multithreading
• Efficient use of CPU.
• Faster execution of tasks.
• Programs become more responsive.
• Easy to model real-world tasks.
• Threads share memory → less resource usage.
Disadvantages of Multithreading
• Programming becomes complex.
• Risk of thread interference (data inconsistency).
• Complex debugging and testing process.
• Deadlocks may occur.
• More memory used if too many threads.
• Unpredictable results.
Creating of threads
• There are two ways to create thread in java:
1. By Extending the Thread class([Link]).
2. By Implementing the Runnable interface ([Link]).
Extending Thread Class
Step 1: Declare the class as extending the Thread class.
Step 2: Implement the run() method that is responsible for
executing the sequence of code that the thread will execute.
Step 3: Create a thread object and call the start() method to
initiate the thread execution.
class A extends Thread
{
---------------------------
---------------------------
---------------------------
}
Implementing the run() method
• The run() method has been inherited by the class A.
• We have to override this method in order to implement
the code to be executed by our thread.
Public void run()
{
--------------------
--------------------
--------------------
}
Starting New Thread
• To create and run an instance of our thread class, we must
need to create object.
• A ob=new A();
• [Link]();
By Implementing Runnable Interface
• Step 1: create a class that implements Runnable interface.
• Step 2: Override run() method to define a task that should be
executed by the Thread.
• Step 3: Create the object of implementation class.
• Step 4: Create the object of thread class and pass
implementation class object reference to Thread constructor.
• Step 5: Use the Thread object to call start() method of the
thread class.
class A extends Thread
{
---------------------------
---------------------------
---------------------------
}
Implementing the run() method
• The run() method has been inherited by the class A.
• We have to override this method in order to implement
the code to be executed by our thread.
Public void run()
{
--------------------
--------------------
--------------------
}
Starting New Thread
• To create and run an instance of our thread class, we must need to
create object.
• A ob=new A();
• Thread t=new Thread(ob);
• [Link]();
Thread Life Cycle
1. New State
• A thread begins in the New state when an object of Thread
class is created, but start() hasn’t been called yet.
• Only after calling start(), it can move to Runnable.
2. Runnable State
• Once start() is called, the thread enters Runnable state.
• The thread is now eligible for execution but waits for CPU
scheduling.
• Multiple threads can be in Runnable at the same time.
• Key methods: start()
[Link](); // moves to Runnable
3. Running State
• The thread scheduler picks one thread from the Runnable
pool and gives it CPU time.
• The thread executes its run() method.
• Only one thread per CPU core can be Running at a time.
• Key method: run() (executed internally, not called directly).
4. Waiting State
• A thread enters Waiting when it waits indefinitely until
another thread signals it (using notify() or notifyAll()).
• It does not come out of this state automatically.
• Key methods: wait(), notify(), notifyAll()
5. Timed Waiting State / Blocked State
• A thread can enter Timed Waiting for a specific time,
after which it moves back to Runnable automatically.
• Common scenarios: sleep(), join(time), wait(time)
• Key methods:
• sleep(milliseconds)
• join(milliseconds)
• wait(milliseconds)
6. Terminated (Dead State)
• Once the run() method finishes, the thread moves to
Terminated.
• It cannot be restarted (start() on a terminated thread throws
IllegalThreadStateException).
What is Synchronization in Threads?
• In Java multithreading, synchronization is a technique to
control the access of multiple threads to shared resources
(like variables, objects, files, or databases).
Without synchronization:
• Two or more threads may access the same resource at the
same time.
• This can lead to data inconsistency, race conditions, or
unexpected results.
• Synchronization ensures only one thread at a time can
access the resource.
Why Synchronization is Needed?
• To avoid race conditions (when multiple threads try to
modify shared data at the same time).
• To ensure thread safety (data remains consistent and
correct).
• To control resource sharing among threads.
How to Achieve Synchronization in Java
• Java provides the synchronized keyword.
• Synchronized Method
• Entire method is locked for a single thread at a time.
class Table {
synchronized void printTable(int n) { // synchronized method
for (int i = 1; i <= 5; i++) {
[Link](n * i);
}
}
}
public class SyncDemo {
public static void main(String[] args) {
Table obj = new Table(); // Thread 1
Thread t1 = new Thread(() -> [Link](5));
Thread t2 = new Thread(() -> [Link](10)); // Thread 2
[Link]();
[Link]();
}
}
• printTable() is synchronized, so only one thread at a time
can use it.
• If you remove synchronized, both threads run together and
the numbers mix.
• With synchronized, first table of 5 prints completely, then
table of 10.
Synchronized Block
class Table {
void printTable(int n) {
synchronized(this) { // only this block is synchronized
for (int i = 1; i <= 5; i++) {
[Link](n * i);
}
}
}
}
public class SyncBlockDemo {
public static void main(String[] args) {
Table obj = new Table();
Thread t1 = new Thread(() -> [Link](5)); // Thread 1
Thread t2 = new Thread(() -> [Link](10)); // Thread 2
[Link]();
[Link]();
}
}
• Here, only the for loop is inside the synchronized(this) block.
• That means only that part is locked for one thread at a time.
• Just like with synchronized method, the output will not mix.
• If you remove the synchronized block, outputs from both threads
will mix together.
Errors in Java
• In Java, errors are problems that occur during program execution or
compilation which stop the program from running correctly.
• Errors are mainly of two categories:
1. Compile-time Errors
2. Runtime Errors
1. Compile-time Errors
• These occur when you compile the program.
• They are detected by the Java compiler.
• The program will not run until you fix them.
Examples:
• Syntax Errors – missing ;, misspelling a keyword.
• Type Errors – assigning int to a String.
• Missing file/class errors.
public class Test {
public static void main(String[] args) {
int a = 10
[Link](a); // Error: missing semicolon
}
}
2. Runtime Errors
• These occur while the program is running.
• They are caused by wrong input, logic, or system issues.
• Java throws exceptions for these errors.
Examples:
• Divide by zero → ArithmeticException
• Array out of bounds → ArrayIndexOutOfBoundsException
• Null value used → NullPointerException
public class Test {
public static void main(String[] args) {
int a = 10 / 0; // Runtime Error: ArithmeticException
}
}
Logical Error in Java
• A logical error is a mistake in the program’s logic.
• The program runs successfully (no compile error, no runtime
error).
• But the output is wrong because the formula or condition is
incorrect.
• These are also called bugs.
• They are the hardest to find because the program looks fine
but gives wrong results.
Example 1: Wrong Formula
public class LogicalErrorExample {
public static void main(String[] args) {
int a = 5, b = 3;
int sum = a - b; // Logic mistake: should be a + b
[Link]("Sum = " + sum);
}
}
Example 2: Wrong Condition
public class LogicalErrorExample2 {
public static void main(String[] args) {
int age = 15;
if (age > 18) { // Logic mistake: should be age >= 18
[Link]("You can vote");
} else {
[Link]("You cannot vote");
}
}
}
• Compiler does not detect logical errors.
• They produce wrong results, not program crashes.
• They must be found by testing and checking output carefully.
Exception in Java
• An exception in Java is an unexpected event or error that
occurs during the execution of a program, which disrupts
the normal flow of instructions.
• Dividing a number by zero
• Accessing an invalid array index
• Opening a file that doesn’t exist
Types of Exceptions in Java
1. Checked Exceptions
2. Unchecked Exceptions (Runtime Exceptions)
1. Checked Exceptions
• These exceptions are checked at compile-time.
• The compiler ensures that you either handle them using try-catch or
declare them using throws.
• They occur due to external issues (like files, databases, or network).
Exception Description
Attempt to create an object of
InstatiationException
an abstract class or interfaces
FileNotFoundException File not found on disk
SQLException Error during database access
ClassNotFoundException Class not found while loading
Thread interrupted while
InterruptedException
sleeping or waiting
IllegalAccessException Access to class is denied
2. Unchecked Exceptions (Runtime Exceptions)
These exceptions occur while the program is running.
They are not checked by the compiler.
Usually caused by logic errors or invalid user input.
Exception Description
ArithmeticException Division by zero
NullPointerException Using a null object reference
ArrayIndexOutOfBoundsException Invalid array index
Invalid string to number
NumberFormatException
conversion
StringIndexOutOfBoundsException Invalid string index access
class UncheckedExample {
public static void main(String[] args) {
int a = 10;
int b = 0;
int result = a / b; // ArithmeticException
[Link]("Result: " + result);
[Link]("Program continues...");
}
}
Exception in thread "main" [Link]: / by zero
How to handle exception
• Exception can be handled by using try and catch block.
• Try block is used to write risky line of code.
• Risky line of code is nothing but the code which is reason for
exception.
• Try block throw the exception to a catch block.
• Catch block is used to catch the exception object which is
thrown by the try block and it execute alternate code instead
of exception.
Syntax of Exception Handling in Java
• Java provides several keywords to handle exceptions safely and
effectively.
• The basic syntax uses try, catch, finally, throw, and throws.
1. Basic Try-Catch Syntax
try
{
// Code that may cause an exception
}
catch (ExceptionType e)
{
// Code to handle the exception
}
class TryCatchExample {
public static void main(String[] args) {
try {
int a = 10 / 0; // This will cause ArithmeticException
} catch (ArithmeticException e) {
[Link]("Cannot divide by zero!");
}
[Link]("Program continues after handling the exception.");
}
}
Cannot divide by zero!
Program continues after handling the exception.
Nested try Statement
• The outer try block contains another inner try block.
• When an exception occurs inside the inner block, its inner
catch handles it.
• If an exception occurs outside the inner block, the outer
catch handles it.
• This structure allows for specific handling of different
exceptions in different sections of the code.
class NestedTryExample {
public static void main(String[] args) {
try {
[Link]("Outer try block starts.");
// Outer try block code
int num = 10 / 2;
[Link]("Result: " + num);
// Inner try block
try {
int[] arr = {1, 2, 3};
[Link](arr[5]); // Causes ArrayIndexOutOfBoundsException
}
catch (ArrayIndexOutOfBoundsException e) {
[Link]("Inner catch: Array index out of range!");
}
// Another operation that causes an exception
int result = 10 / 0; // Causes ArithmeticException
}
catch (ArithmeticException e) {
[Link]("Outer catch: Division by zero!");
}
[Link]("Program continues normally...");
}
}
Outer try block starts.
Result: 5
Inner catch: Array index out of range!
Outer catch: Division by zero!
Program continues normally...
class MultipleCatchExample {
public static void main(String[] args) {
try {
int[] arr = new int[5];
arr[5] = 30 / 0; // This line can cause two exceptions
}
catch (ArithmeticException e) {
[Link]("Arithmetic Exception: Division by zero!");
}
catch (ArrayIndexOutOfBoundsException e) {
[Link]("Array Index Out Of Bounds Exception!");
}
catch (Exception e) {
[Link]("General Exception!");
}
[Link]("Program continues normally...");
}
}
• The try block may cause different types of exceptions.
• Each catch block handles a specific exception type.
• When 10 / 0 runs, an ArithmeticException occurs → handled
by the first catch.
• If that didn’t occur but an array index error did, the second
catch would handle it.
• The last catch (Exception e) is a general catch, used for any
other exceptions.
finally Block in Java
• The finally block in Java is used to guarantee that certain
code always runs, no matter what happens in the try or
catch blocks — even if an exception is thrown or the method
returns early.
Syntax
try {
// Code that might throw an exception
} catch (ExceptionType e) {
// Handle the exception
} finally {
// This block always runs
}
public class FinallyDemo {
public static void main(String[] args) {
try {
[Link]("Inside try block");
int result = 10 / 0; // This will cause ArithmeticException
} catch (ArithmeticException e) {
[Link]("Caught an exception: " + [Link]());
} finally {
[Link]("This is the finally block — always runs!");
}
[Link]("Program continues after try-catch-finally.");
}
}
Output
Inside try block
Caught an exception: / by zero
This is the finally block — always runs!
Program continues after try-catch-finally.