What is a Thread?
A thread is a path of execution within a program.
In simple words: A program has at least one thread (called the main thread), and you can create more to do multiple things
at once.
Real-life analogy:
Imagine you're cooking food while listening to music and downloading a file in the background these are multiple tasks
happening in parallel, just like threads in a program.
Why Use Threads?
Threads help you:
Do things in parallel (like downloading a file while showing a progress bar)
Improve performance for I/O-heavy or waiting tasks
Make apps more responsive (e.g., UIs)
How to Create a Thread
There are two common ways to create a thread in Java.
a) Extending the Thread class
run() method contains the code you want to run in the new thread.
start() actually starts the thread and calls run() behind the scenes.
b) Implementing the Runnable interface
This is better practice because Java supports single inheritance. If you extend Thread, you can't extend any other class.
What is the Thread Lifecycle?
A thread goes through below states:
State Meaning
New Thread is created but not started yet.
Runnable Thread is ready and waiting for CPU.
Running Thread is currently executing.
Blocked Thread is waiting for a lock/monitor.
Waiting Thread is waiting indefinitely for another thread.
Timed Waiting Thread waits for a specific time (e.g., sleep, join(timeout))
Terminated Thread has completed execution or exited due to an
exception.
List of Useful Java Thread Methods
1. start()
What it does: Starts a new thread and calls the run() method in the background.
2. run()
What it does: Defines the code that should run in the thread.
Note: You should not call run() directly use start() instead.
3. sleep(long milliseconds)
What it does: Puts the current thread to sleep for the given time (in ms).
Useful when simulating a delay.
4. join()
What it does: Makes one thread wait for another to finish.
5. isAlive()
What it does: Returns true if the thread is still running.
6. getName() / setName(String)
What it does: Gets or sets the name of the thread.
7. getId()
What it does: Returns the unique ID of a thread.
8. getPriority() / setPriority(int)
What it does: Gets or sets the priority of a thread (1 to 10).
Note: Thread priority is just a hint to the OS scheduler, not a guarantee.
MIN_PRIORITY = 1
NORM_PRIORITY = 5
MAX_PRIORITY = 10
9. isDaemon() / setDaemon(boolean)
What it does: A daemon thread runs in the background (e.g., garbage collection) and doesn't block JVM shutdown.
You must call setDaemon(true) before calling start().
10. interrupt()
What it does: Interrupts a thread usually used to signal a thread to stop.
11. isInterrupted()
What it does: Checks whether the thread has been interrupted.
12. currentThread()
What it does: Returns the currently running thread.
Very useful for debugging or naming log output.
13. yield()
What it does: Suggests the currently executing thread to pause and allow other threads of equal priority to execute.
Note: It's just a hint, not a guarantee. Often used in tight CPU loops to be polite.
Summary:
Method Use Case
start() Begin thread execution
run() Define the thread task
sleep() Pause execution
join() Wait for another thread
isAlive() Check if thread is still running
get/setName() Identify threads
getId() Unique ID for thread
get/setPriority() Set execution preference
setDaemon() Mark as background thread
interrupt() Ask thread to stop
isInterrupted() Check if thread is interrupted
currentThread() Get current thread reference
yield() Pause to give chance to others
Thread Example – Download + Loading Animation
This simulates:
A file downloading (Thread 1)
A loading animation shown during download (Thread 2)
JVM Thread Scheduler: The Real Picture
1. JVM Does Not Have Its Own Scheduler
The Java Virtual Machine (JVM):
Creates and manages threads using Java APIs (Thread, Runnable, etc.)
Keeps track of thread state, metadata, stack memory, and locks
But does not control actual CPU execution time
Instead, it relies entirely on the OS thread scheduler
JVM ≠ Thread scheduler
JVM = Thread manager + lifecycle tracker
2. OS Thread Scheduling: The Real Scheduler
The OS uses a thread scheduler algorithm. Java threads are mapped 1:1 with native OS threads using system calls like:
pthread_create() on Unix/Linux
CreateThread() on Windows
3. Types of Scheduling Used by OS (and leveraged by JVM)
The actual scheduling algorithm depends on the OS. JVM threads benefit from these algorithms.
A. Preemptive Scheduling
Every thread gets a small time slice (quantum)
OS forcibly switches to another thread when the time is up
Used by almost all modern OS schedulers
Ensures fairness and avoids starvation
B. Priority-Based Scheduling
Threads have priorities (Java: 1 to 10)
Higher priority threads get more CPU time (if the OS respects it)
May lead to starvation of lower-priority threads
4. Common OS Scheduling Algorithms Used
OS Scheduling Algorithm Java Thread Support
Linux Completely Fair Scheduler (CFS) YES
Windows Multilevel Feedback Queue (MLFQ) YES
macOS Hybrid of Round Robin and Priority YES
Queue
A. Example: Linux – Completely Fair Scheduler (CFS)
Each thread has a vruntime (virtual runtime)
Scheduler picks the thread with lowest vruntime (i.e., least CPU usage so far)
Tries to give fair time to all threads
CFS Tree (Red-Black Tree):
Java threads on Linux run inside this structure. JVM priority hints are ignored unless nice values or sched_setscheduler
is manually adjusted.
B. Example: Windows – Multilevel Feedback Queue (MLFQ)
Several queues for different priority levels
Threads move between queues based on behavior (CPU-bound vs IO-bound)
High priority threads preempt low priority ones
If you set a thread priority in Java (e.g. Thread.MAX_PRIORITY), Windows may give it a better queue.
5. JVM’s Role in Scheduling (Simplified View)
Although the OS decides when a thread runs, the JVM:
Tracks thread states (NEW, RUNNABLE, BLOCKED, etc.)
Passes RUNNABLE threads to the OS queue
Wakes up threads from WAITING / TIMED_WAITING when time expires or notify happens
Keeps a monitor lock table to decide which thread to unblock first
6. Java Thread Priorities - Just Hints
Java threads can have 3 predefined constants:
But:
On Linux, it’s usually ignored unless you change the thread policy.
On Windows, it can influence thread queue placement.
7. Summary Table: JVM vs OS Responsibilities
Responsibility JVM OS
Create thread object ✅ ❌
Map to native thread ✅ ✅
Schedule CPU time ❌ ✅
Handle start() and join() ✅ ❌
Respect setPriority() Hint only May respect
Detect deadlocks ✅ ❌
Context switching ❌ ✅
Kill/halt threads ✅ (deprecated) ✅
Thread finishes -> JVM sets state TERMINATED
9. What Can You Control?
Use Executors for controlled thread pools
Use Thread.setPriority() as a soft hint
Use Thread.sleep() or Thread.yield() to influence timing
For real control, use native methods or OS-level configurations (like nice, sched in Linux)
Summary
JVM does not contain its own thread scheduler, but uses the OS thread scheduler instead. Java threads are managed by the
JVM and executed by the OS based on preemptive and priority-based scheduling algorithms like CFS or MLFQ.
Thread Synchronization in Java
What is Synchronization?
When two or more threads try to access the same shared resource at the same time, data inconsistency can occur.
To prevent this, Java provides synchronization, which ensures that only one thread can access the critical section (shared
code/data) at a time.
Real-Life Analogy:
Think of a public washroom with a single key. Only one person can use it at a time. Others must wait.
That’s what synchronization does lock access to a shared resource until the current thread is done.
Problem Without Synchronization
You expect 2000, but you might get 1850, 1933, etc. because both threads are modifying count at the same time.
Solving It With Synchronization
Now only one thread at a time can enter the increment() method. This guarantees correct output: 2000
Types of Synchronization:
Method-level
Block-level (more fine-grained)
Static synchronization (locks on class-level)
Important Notes
Avoid over-synchronization: It can slow down your app.
Only use synchronization when multiple threads access shared data.
Always prefer block-level sync for better performance (lock only what's needed).
Summary
Use synchronization to protect shared resources.
synchronized ensures mutual exclusion.
You can synchronize methods or blocks.
Always balance safety and performance.
Deadlock in Java Threads What It Is, Why It Happens, and How to Avoid It
What is a Deadlock?
A deadlock is a situation where two or more threads are waiting for each other to release resources, but none of them ever
does. As a result, all the involved threads are stuck forever.
Real-Life Analogy
Imagine two people:
Person A holds Spoon and waits for Fork
Person B holds Fork and waits for Spoon
Since neither will give up what they’re holding, both are stuck this is exactly how deadlocks work in code.
How Deadlock Happens in Java (Code Example)
What happens here?
Thread 1 locks r1 and waits for r2
Thread 2 locks r2 and waits for r1
They’re both waiting forever = deadlock.
Four Conditions for Deadlock (All must be true)
1. Mutual Exclusion – Resources can't be shared
2. Hold and Wait – Thread holds one resource and waits for another
3. No Preemption – You can’t forcibly take resource from thread
4. Circular Wait – A waits for B, B waits for C, C waits for A
How to Detect Deadlock
Java doesn't throw a DeadlockException, but you can detect it using tools like:
jConsole
VisualVM
Thread Dump (analyze using jstack)
How to Avoid Deadlocks (Practical Solutions)
1. Lock Resources in Same Order
Always acquire locks in a fixed global order.
If all threads follow the same order, circular wait won't occur.
2. Use Try-Lock with Timeout (java.util.concurrent.locks.Lock)
Use ReentrantLock.tryLock() to avoid indefinite waiting.
This avoids deadlock because if a thread can’t get both locks in time, it backs off.
3. Avoid Nested Locks If Possible
The more nested locks you have, the higher the risk of deadlock.
Try to minimize deep nesting in critical sections.
4. Use Higher-Level Concurrency APIs
Use ExecutorService, synchronized collections, ConcurrentHashMap, BlockingQueue, etc., which handle low-level locking
internally.
Summary
Concept Explanation
What is Deadlock? Two or more threads waiting for each other forever
Real-life Example Two people holding one spoon/fork each, waiting for the
other
How it Happens Nested synchronized blocks with shared resources
Detection Use jConsole, jstack, thread dumps
Prevention Lock ordering, tryLock, timeout, avoid nesting, use high-
level APIs
Deadlocks are tricky but avoidable.
The key is to analyze your locking strategy, use timeouts where possible, and lean on modern concurrency utilities that are
designed to avoid such pitfalls.