Multithreading with JVM Architecture
In modern software applications, multiple tasks often need to run at the same time. For example, a web server may handle many user requests together, a media application may play audio while updating the screen, and a chat application may send and receive messages simultaneously. Java supports such parallel task execution through multithreading.
Multithreading is one of Java’s most powerful features, and it becomes even more important when understood together with JVM architecture. The JVM manages threads, stack memory, program counters, synchronization, and execution flow, which makes multithreading possible and efficient.
What is a Thread?
A thread is the smallest unit of execution inside a process. A Java program runs as a process, and inside that process there can be one or more threads.
In simple words:
- a process is a running program
- a thread is an individual path of execution inside that program
Every Java program has at least one thread, called the main thread.
What is Multithreading?
Multithreading is the process of executing multiple threads concurrently within the same program.
This means a single Java application can perform several tasks at the same time, such as:
- reading data from a file
- processing user input
- updating the UI
- performing background calculations
Why Multithreading is Needed
- improves responsiveness of applications
- better CPU utilization
- allows background processing
- supports concurrent task execution
- improves performance in many real-world applications
Real-World Example of Multithreading
Consider a music application:
- one thread plays the song
- another thread updates the progress bar
- another thread listens for user input like pause or next
If only one thread were used, the application could become slow or unresponsive.
Single Thread vs Multithreading
| Point | Single Thread | Multithreading |
|---|---|---|
| Execution flow | One task at a time | Multiple tasks concurrently |
| Responsiveness | May block easily | Better responsiveness |
| Resource usage | Simpler but limited | More efficient in many cases |
| Complexity | Low | Higher |
How to Create Threads in Java
Java mainly provides two common ways to create threads:
- by extending the
Threadclass - by implementing the
Runnableinterface
1. Creating Thread by Extending Thread Class
Here:
run()contains the code that the thread will executestart()creates a new thread and internally callsrun()
2. Creating Thread by Implementing Runnable Interface
This approach is generally preferred because Java does not support multiple inheritance, so implementing Runnable keeps the design more flexible.
Difference Between start() and run()
This is one of the most common interview questions.
run()executes like a normal method callstart()creates a new thread and then callsrun()internally
Example
Using run() directly does not create a separate thread.
Thread Lifecycle in Java
A thread passes through different states during execution.
- New
- Runnable
- Running
- Blocked / Waiting
- Terminated
1. New
Thread object is created but not started yet.
2. Runnable
After calling start(), the thread becomes eligible for execution.
3. Running
The thread is currently executing on CPU.
4. Waiting / Blocked
The thread is waiting for some resource, lock, or condition.
5. Terminated
The thread has completed execution.
Thread Scheduler
Java uses a thread scheduler to decide which thread should run at what time. The exact scheduling depends on the JVM and operating system.
This is why thread execution order is not always predictable.
JVM Architecture and Multithreading
The JVM architecture directly supports multithreading through its runtime data areas and execution engine.
Important JVM areas related to threads:
- Heap → shared by all threads
- Method Area → shared by all threads
- Java Stack → one separate stack per thread
- PC Register → one separate program counter per thread
- Native Method Stack → one per thread for native methods
Shared Memory in JVM
Some memory areas are common to all threads:
- Heap → stores objects and instance variables
- Method Area → stores class metadata and static members
Because these areas are shared, multiple threads can access the same object or static variable.
Thread-Specific Memory in JVM
Each thread gets its own:
- Java Stack
- PC Register
- Native Method Stack
This is why local variables are usually thread-safe—they exist in each thread’s private stack.
Example: Shared Object Access
When multiple threads access the same object in heap memory, data inconsistency can occur if synchronization is not used.
If two threads call increment() simultaneously, they may produce unexpected results because both are working on shared heap data.
Thread Methods in Java
The Thread class provides many useful methods:
start()run()sleep()join()yield()getName()setName()currentThread()
sleep() Method
The sleep() method pauses the current thread for a specified time.
Here, the thread sleeps for 1 second between prints.
join() Method
The join() method makes one thread wait until another thread completes.
The main thread waits until the child thread finishes.
currentThread() Method
This method returns the currently executing thread.
Thread Priorities
Java allows setting priorities for threads. Priority values usually range from 1 to 10.
Thread.MIN_PRIORITY= 1Thread.NORM_PRIORITY= 5Thread.MAX_PRIORITY= 10
However, actual scheduling still depends on JVM and OS.
Synchronization in Java
When multiple threads access shared resources, synchronization is used to prevent inconsistent results.
The synchronized keyword ensures that only one thread can access a synchronized block or method at a time for the same object.
Without Synchronization
Shared data may be modified unpredictably.
With Synchronization Example
Now only one thread can execute increment() at a time for the same object.
Why Synchronization is Needed
Consider two threads updating the same bank balance. Without synchronization:
- both may read the same old value
- both may update incorrectly
- final result may become wrong
Synchronization ensures safe access to shared data.
Thread Communication
Java provides methods like:
wait()notify()notifyAll()
These are used for communication between threads working on shared objects.
Daemon Thread
A daemon thread is a background thread that provides services to other threads.
Example:
- garbage collector thread
When all user threads finish, daemon threads also stop automatically.
Example of Multithreading
Output order may vary because both threads run concurrently.
How JVM Supports Thread Execution
- each thread gets its own stack
- each thread gets its own program counter
- shared heap allows object sharing
- thread scheduler manages execution order
- monitor locks support synchronization
Common Multithreading Problems
1. Race Condition
Occurs when multiple threads update shared data at the same time without proper synchronization.
2. Deadlock
Occurs when two or more threads wait forever for each other’s locks.
3. Starvation
Occurs when a thread never gets CPU time or required resources.
4. Thread Interference
Happens when threads overwrite each other’s changes in shared data.
Best Practices in Multithreading
- use synchronization only where needed
- keep shared mutable data minimal
- prefer implementing
Runnablefor flexibility - avoid holding locks longer than necessary
- understand JVM memory sharing between threads
Common Mistakes
- calling
run()instead ofstart() - sharing data without synchronization
- assuming thread execution order is fixed
- forgetting that stack is thread-specific but heap is shared
- using too many threads unnecessarily
Interview-Oriented Points
- Multithreading allows concurrent task execution
- Thread can be created using
Threadclass orRunnableinterface start()creates a new thread,run()is just a normal method if called directly- Each thread has its own stack and PC register
- Heap and method area are shared across threads
- Synchronization is needed for shared mutable data
- Common issues are race condition, deadlock, and starvation
- JVM architecture directly supports multithreading through separate stacks and shared heap
Conclusion
Multithreading is one of the most powerful capabilities of Java because it allows programs to perform multiple tasks concurrently and use system resources more efficiently. It is widely used in servers, desktop applications, background processing systems, and enterprise software.
Understanding multithreading together with JVM architecture gives a much deeper understanding of how Java actually manages execution, memory, and concurrency. This knowledge is highly valuable in both interviews and real-world Java development.

