Multithreading with JVM Architecture

Java 15 min min read Updated: Mar 31, 2026 Advanced
Multithreading with JVM Architecture
Advanced Topic 5 of 14

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.

Key Concept: Multithreading allows multiple tasks to execute concurrently within a Java program, and the JVM architecture supports it through thread scheduling, stack management, program counters, and synchronized object monitoring.

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 Thread class
  • by implementing the Runnable interface

1. Creating Thread by Extending Thread Class

java class MyThread extends Thread { public void run() { System.out.println("Thread is running"); } } public class Main { public static void main(String[] args) { MyThread t = new MyThread(); t.start(); } }

Here:

  • run() contains the code that the thread will execute
  • start() creates a new thread and internally calls run()

2. Creating Thread by Implementing Runnable Interface

java class MyTask implements Runnable { public void run() { System.out.println("Runnable thread is running"); } } public class Main { public static void main(String[] args) { MyTask task = new MyTask(); Thread t = new Thread(task); t.start(); } }

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 call
  • start() creates a new thread and then calls run() internally

Example

java class MyThread extends Thread { public void run() { System.out.println("Inside run"); } } public class Main { public static void main(String[] args) { MyThread t = new MyThread(); t.run(); // normal method call t.start(); // new thread starts } }

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.

Important: Heap is shared, but stack is separate for each thread. This is one of the most important concepts in Java multithreading.

Example: Shared Object Access

When multiple threads access the same object in heap memory, data inconsistency can occur if synchronization is not used.

java class Counter { int count = 0; void increment() { count++; } }

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.

java class MyThread extends Thread { public void run() { try { for (int i = 1; i <= 3; i++) { System.out.println(i); Thread.sleep(1000); } } catch (InterruptedException e) { System.out.println("Thread interrupted"); } } }

Here, the thread sleeps for 1 second between prints.

join() Method

The join() method makes one thread wait until another thread completes.

java class MyThread extends Thread { public void run() { for (int i = 1; i <= 3; i++) { System.out.println("Child Thread: " + i); } } } public class Main { public static void main(String[] args) throws Exception { MyThread t = new MyThread(); t.start(); t.join(); System.out.println("Main thread continues"); } }

The main thread waits until the child thread finishes.

currentThread() Method

This method returns the currently executing thread.

java public class Main { public static void main(String[] args) { Thread t = Thread.currentThread(); System.out.println(t.getName()); } }

Thread Priorities

Java allows setting priorities for threads. Priority values usually range from 1 to 10.

  • Thread.MIN_PRIORITY = 1
  • Thread.NORM_PRIORITY = 5
  • Thread.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

java class Counter { int count = 0; synchronized void increment() { count++; } }

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

java class Task1 extends Thread { public void run() { for (int i = 1; i <= 3; i++) { System.out.println("Task1: " + i); } } } class Task2 extends Thread { public void run() { for (int i = 1; i <= 3; i++) { System.out.println("Task2: " + i); } } } public class Main { public static void main(String[] args) { Task1 t1 = new Task1(); Task2 t2 = new Task2(); t1.start(); t2.start(); } }

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 Runnable for flexibility
  • avoid holding locks longer than necessary
  • understand JVM memory sharing between threads

Common Mistakes

  • calling run() instead of start()
  • 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 Thread class or Runnable interface
  • 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.

Quick Summary: Multithreading allows multiple threads to run concurrently in Java, and the JVM supports this through thread-specific stacks and PC registers, shared heap memory, synchronization mechanisms, and thread scheduling.

Get Newsletter

Subscibe to our newsletter and we will notify you about the newest updates on Edugators