Topic 95 of 119 11. Multithreading in Java Interactive Tutorial

Java Multithreading: Concurrency Fundamentals, Threads, and Lifecycle

10 mins read Last updated: 22 Aug 2026 31,435 views

Direct Answer & Core Concept

Java multithreading allows a program to perform multiple tasks concurrently. Instead of making a program wait for one task to completely finish before starting another, different threads can make progress independently.

Multithreading is useful for applications that perform background processing, network operations, file handling, user-interface work, server requests, and other tasks that can benefit from concurrency.

To understand Java concurrency properly, you first need to understand threads, processes, thread creation, thread states, lifecycle, scheduling, and basic thread methods.

Detailed Explanation & Mechanics

What Is Multithreading in Java?

Multithreading is the execution of multiple threads within a single Java application.

A thread is a lightweight unit of execution.

For example, a web application might have different work happening at the same time:

Architecture Diagram
Java Application
       |
       +── Thread 1 → Handle Request
       |
       +── Thread 2 → Process File
       |
       +── Thread 3 → Perform Background Task

These threads share the resources of the same process while having their own execution path.


What Is a Thread?

A thread is an independent path of execution within a program.

Every Java application starts with at least one thread: the main thread.

Consider:

Main.java
Run Online
public class Main {
    public static void main(String[] args) {
        System.out.println("Hello Java");
    }
}

The main() method runs on the main thread.

Conceptually:

Architecture Diagram
Java Program
     |
     └── Main Thread
            |
            └── main()

You can create additional threads when your application needs concurrent work.


Process vs Thread

A process is a running program with its own operating-system resources.

A thread is an execution path within a process.

A simple comparison:

ProcessThread
Larger unit of executionSmaller unit of execution
Has its own process resourcesShares many process resources
More expensive to createGenerally cheaper to create
Processes are isolated from each otherThreads within a process share memory
Can contain multiple threadsBelongs to a process

For example:

Architecture Diagram
Application Process
        |
        +── Thread 1
        +── Thread 2
        +── Thread 3

Concurrency vs Parallelism

These two terms are related but not identical.

Concurrency

Concurrency means multiple tasks can make progress during overlapping periods.

For example:

Main.java
Run Online
Task A: ███ ███Task B: ███ ███

The tasks may take turns.

Parallelism

Parallelism means multiple tasks are actually executing at the same time, typically on different CPU cores.

Main.java
Run Online
Core 1 → Task ACore 2 → Task B

Therefore:

Concurrency is about dealing with multiple tasks; parallelism is about executing multiple tasks simultaneously.

A multithreaded Java program may be concurrent, parallel, or both depending on the runtime environment and hardware.


Why Use Multithreading?

Multithreading can be useful when an application has independent or blocking tasks.

Common examples include:

  • web servers

  • background jobs

  • file processing

  • network requests

  • database operations

  • downloading files

  • scheduled tasks

  • GUI applications

  • simulations

  • CPU-intensive calculations

For example, a program might download several independent files concurrently:

Architecture Diagram
Main Program
     |
     +── Thread 1 → Download File A
     +── Thread 2 → Download File B
     +── Thread 3 → Download File C

Creating a Thread in Java

There are several ways to perform work concurrently in Java.

The classic approaches include:

  1. Extending Thread

  2. Implementing Runnable

Modern Java applications also commonly use:

  1. ExecutorService

  2. Callable and Future

  3. Higher-level concurrency utilities

For understanding the fundamentals, start with Thread and Runnable.


Creating a Thread by Extending Thread

You can create a thread by extending the Thread class.

Main.java
Run Online
class MyThread extends Thread {
    @Override public void run() {
        System.out.println("Thread is running");
    }
}

Then create the thread:

Main.java
Run Online
public class Main {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
    }
}

The important method here is:

The run() method contains the work performed by the thread.


Why Use start() Instead of run()?

This is one of the most important beginner concepts.

Correct:

Not:

Calling:

asks the JVM to start a new thread of execution.

Calling:

is simply a normal method call executed by the current thread.

For example:

Main.java
Run Online
MyThread thread = new MyThread();
thread.run();

does not create a new thread.

The distinction is:

Main.java
Run Online
start()↓New thread execution↓run()

Whereas:

Main.java
Run Online
run()↓Normal method call

Creating a Thread with Runnable

A more flexible traditional approach is to implement Runnable.

Main.java
Run Online
class MyTask implements Runnable {
    @Override public void run() {
        System.out.println("Task is running");
    }
}

Then:

Main.java
Run Online
public class Main {
    public static void main(String[] args) {
        Runnable task = new MyTask();
        Thread thread = new Thread(task);
        thread.start();
    }
}

Here:

Main.java
Run Online
Runnable↓Defines the taskThread↓Runs the task

This separation between task and thread is often preferable to extending Thread.


Runnable with Lambda Expression

Because Runnable is a functional interface, you can use a lambda expression.

Main.java
Run Online
public class Main {
    public static void main(String[] args) {
        Runnable task =() -> {
            System.out.println("Task is running");
        };
        Thread thread = new Thread(task);
        thread.start();
    }
}

This is a concise modern Java style.

You can also write:

Main.java
Run Online
Thread thread = new Thread(() -> System.out.println("Task is running"));
thread.start();

Running Multiple Threads

You can create multiple threads.

Main.java
Run Online
public class Main {
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> System.out.println("Task 1"));
        Thread thread2 = new Thread(() -> System.out.println("Task 2"));
        thread1.start();
        thread2.start();
    }
}

The output order is not guaranteed.

You might see:

or:

The JVM and operating system determine how threads are scheduled.


Thread Scheduling

When multiple threads are ready to run, the operating system and JVM work together to determine when each thread gets CPU time.

For example:

Main.java
Run Online
Time →Thread A: ███ ███Thread B: ███ ███

You should not generally write code that depends on an exact scheduling order unless you explicitly establish that ordering through appropriate synchronization or coordination mechanisms.

This is one reason concurrent programs can produce different output between runs.


Thread Lifecycle

A Java thread moves through several states during its lifetime.

Java represents these states using:

The official states are:

Main.java
Run Online
NEWRUNNABLEBLOCKEDWAITINGTIMED_WAITINGTERMINATED

A simplified lifecycle looks like:

Main.java
Run Online
NEW | start()↓RUNNABLE / \ / \BLOCKED WAITING | | | | +----→ RUNNABLE |↓TERMINATED

The exact transitions depend on what the thread is doing.


NEW State

A thread is in the NEW state after the Thread object has been created but before start() has been called.

Example:

Main.java
Run Online
Thread thread = new Thread(() -> {
    System.out.println("Running");
});

At this point:

The thread has not started execution yet.


RUNNABLE State

After calling:

the thread becomes eligible to run.

Its Java thread state is:

RUNNABLE represents a thread that is ready to run or is actually running; Java's state model does not have a separate RUNNING state.

The operating system scheduler determines when it actually gets CPU time.


BLOCKED State

A thread enters BLOCKED when it is waiting to acquire a monitor lock required to enter a synchronized block or method.

For example:

Main.java
Run Online
synchronized (lock) {
    // protected work
}

If another thread already owns the same monitor, a competing thread may become:

This state is mainly relevant when studying synchronization.


WAITING State

A thread is in WAITING when it is waiting indefinitely for another thread or action to wake it.

Examples include:

and:

without a timeout.

A waiting thread remains waiting until an appropriate event occurs.


TIMED_WAITING State

A thread enters TIMED_WAITING when it waits for a specified period.

Examples include:

and:

The thread waits for a limited amount of time.


TERMINATED State

A thread reaches TERMINATED after its execution has completed.

For example:

Main.java
Run Online
Thread thread = new Thread(() -> {
    System.out.println("Work completed");
});
thread.start();

After the run() method finishes, the thread terminates.

A terminated thread cannot be started again.

Calling:

a second time results in:

Main.java
Run Online
IllegalThreadStateException

Checking a Thread's State

You can use:

Example:

Main.java
Run Online
Thread thread = new Thread(() -> {
    System.out.println("Running");
});
System.out.println(thread.getState());
thread.start();
System.out.println(thread.getState());

The exact second state depends on timing. It may already have completed by the time you check it.

This is an important lesson:

Thread state can change quickly, so observations made from another thread are inherently timing-dependent.


Thread Names

Giving threads meaningful names can make debugging easier.

You can set a name:

Main.java
Run Online
Thread thread = new Thread(() -> System.out.println("Working"));
thread.setName("FileProcessor");
thread.start();

Or specify the name during construction:

Main.java
Run Online
Thread thread = new Thread(() -> System.out.println("Working"), "FileProcessor");

You can retrieve the name using:

Main.java
Run Online
System.out.println(thread.getName());

currentThread()

Java provides:

to obtain the thread currently executing the code.

Example:

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

Typical output:

This is useful for debugging and understanding which thread is executing a piece of code.


Thread ID

You can obtain a thread's ID:

Main.java
Run Online
long id = thread.getId();

Example:

Main.java
Run Online
Thread thread = new Thread(() -> {
    System.out.println("Running");
});
System.out.println(thread.getId());

Thread IDs are assigned by the JVM and are useful for diagnostics.


Thread Sleep

Thread.sleep() pauses the current thread for approximately the specified duration.

Example:

Main.java
Run Online
public class Main {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("Start");
        Thread.sleep(1000);
        System.out.println("After one second");
    }
}

The thread enters TIMED_WAITING during the sleep.


sleep() Does Not Release Locks

This is an important distinction.

If a thread is inside a synchronized block and calls:

it does not automatically release the monitor lock it holds.

For example:

Main.java
Run Online
synchronized (lock) {
    Thread.sleep(1000);
}

The thread keeps the lock while sleeping.

This differs from coordination mechanisms such as Object.wait(), which releases the associated monitor while waiting.


Interrupting a Thread

Java provides interruption as a cooperative mechanism for requesting that a thread stop what it is doing or respond to cancellation.

You can request interruption using:

Example:

Main.java
Run Online
Thread worker = new Thread(() -> {
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        System.out.println("Thread interrupted");
        Thread.currentThread().interrupt();
    }
});
worker.start();
worker.interrupt();

An interrupt is not a forced termination mechanism.

The interrupted thread should respond appropriately.


What Is InterruptedException?

Some blocking methods respond to interruption by throwing:

For example:

can throw InterruptedException.

A common practice when you do not have another appropriate recovery strategy is to restore the interrupt status:

Main.java
Run Online
catch (InterruptedException e) {
    Thread.currentThread().interrupt();
}

This preserves the interruption signal for higher-level code.


Joining Threads

Sometimes one thread needs to wait for another thread to finish.

Use:

Example:

Main.java
Run Online
public class Main {
    public static void main(String[] args) throws InterruptedException {
        Thread worker = new Thread(() -> {
            System.out.println("Worker running");
        });
        worker.start();
        worker.join();
        System.out.println("Worker finished");
    }
}

The main thread waits for worker to terminate before continuing past join().


join() with Timeout

You can also specify a maximum waiting time:

The current thread waits for up to approximately 1000 milliseconds.

If the worker finishes earlier, the wait can end earlier.


Thread Priority

Java threads have priorities.

You can set a priority using:

Main.java
Run Online
thread.setPriority(Thread.MAX_PRIORITY);

Java defines constants such as:

Main.java
Run Online
Thread.MIN_PRIORITYThread.NORM_PRIORITYThread.MAX_PRIORITY

However, thread priority should not be used as a reliable mechanism for application correctness or precise scheduling.

Operating-system scheduling and runtime behavior can vary.

Use explicit coordination when correctness depends on ordering.


Daemon Threads

A thread can be marked as a daemon thread:

This must be done before:

Daemon threads are background threads associated with the JVM's lifecycle.

When no live non-daemon threads remain, the JVM can terminate without waiting for daemon threads to finish.

Therefore, daemon threads should not be used for critical work that must complete before application shutdown.


Example of a Daemon Thread

Main.java
Run Online
public class Main {
    public static void main(String[] args) {
        Thread background = new Thread(() -> {
            while (true) {
                System.out.println("Background task");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        });
        background.setDaemon(true);
        background.start();
        System.out.println("Main thread finished");
    }
}

The JVM may exit once the main thread and other non-daemon threads have completed.


Thread Lifecycle Example

The following example demonstrates a simple lifecycle:

Main.java
Run Online
public class Main {
    public static void main(String[] args) throws InterruptedException {
        Thread worker = new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
        System.out.println(worker.getState());
        worker.start();
        System.out.println(worker.getState());
        worker.join();
        System.out.println(worker.getState());
    }
}

A typical sequence is:

The exact intermediate state can vary because thread scheduling is nondeterministic.


Thread Lifecycle Diagram

A useful mental model is:

Architecture Diagram
                    create Thread
                         |
↓
NEW
                         |
                       start()
                         |
↓
RUNNABLE
                    /    |    \
                   /     |     \
↓

↓

↓
BLOCKED  WAITING  TIMED_WAITING
                  \       |       /
                   \      |      /
                    └──→ RUNNABLE
                           |
                           |
                       run() ends
                           |
↓
TERMINATED

Not every thread visits every state.

The transitions depend on what operations the thread performs.


Multiple Threads Example

Consider a program with two worker threads:

Main.java
Run Online
public class Main {
    public static void main(String[] args) {
        Thread task1 = new Thread(() -> {
            for (int i = 1;
            i <= 5;
            i++) {
                System.out.println("Task 1: " + i);
            }
        });
        Thread task2 = new Thread(() -> {
            for (int i = 1;
            i <= 5;
            i++) {
                System.out.println("Task 2: " + i);
            }
        });
        task1.start();
        task2.start();
    }
}

The output may interleave:

Main.java
Run Online
Task 1: 1Task 2: 1Task 2: 2Task 1: 2Task 1: 3Task 2: 3...

The exact order should not be assumed.


Why Output Order Can Change

When multiple threads execute concurrently, the scheduler can switch between them.

Therefore, this:

Main.java
Run Online
thread1.start();
thread2.start();

does not mean:

Main.java
Run Online
Thread 1 completely finishes↓Thread 2 starts

Instead, both threads become eligible to execute.

Architecture Diagram
Thread 1 ──┐
           ├── CPU scheduling
Thread 2 ──┘

If the program requires a particular order, use explicit coordination such as join(), locks, conditions, or higher-level concurrency utilities.


Thread Safety

Multithreading becomes more complicated when multiple threads access the same mutable data.

For example:

If several threads update count concurrently:

the result may not be what you expect.

The operation involves multiple conceptual steps:

Main.java
Run Online
Read count↓Add 1↓Write count

Another thread can interfere between these steps.

This leads to the broader topic of race conditions and synchronization.


Shared Data Example

Suppose two threads access the same object:

Main.java
Run Online
class Counter {
    int count = 0;
    void increment() {
        count++;
    }
}

If multiple threads call:

at the same time, the operation is not automatically safe merely because it looks like one line of Java code.

This is why Java provides synchronization and concurrency utilities.


Multithreading and Memory

Threads within the same Java process can share objects stored in the common application memory.

Conceptually:

Architecture Diagram
Process
 |
 +── Thread 1 ──┐
 |               |
 +── Thread 2 ──┼── Shared Objects
 |               |
 +── Thread 3 ──┘

This shared-memory model makes communication between threads efficient, but it also creates risks when mutable state is accessed without appropriate coordination.


Common Thread Mistakes

Mistake 1: Calling run() Instead of start()

Wrong for starting a new thread:

Correct:


Mistake 2: Starting a Thread Twice

This is invalid:

Main.java
Run Online
thread.start();
thread.start();

A Java Thread instance can be started only once.


Mistake 3: Assuming Thread Order

Do not assume:

Main.java
Run Online
thread1.start();
thread2.start();

means thread 1 always executes first.

Scheduling is not guaranteed that way.


Mistake 4: Using sleep() for Synchronization

This is fragile:

Using a fixed delay does not reliably guarantee that another task has completed.

Prefer explicit coordination such as:

or suitable concurrency mechanisms.


Mistake 5: Ignoring InterruptedException

Avoid silently swallowing interruption:

Main.java
Run Online
catch (InterruptedException e) {
}

When appropriate, preserve the interrupt:

Main.java
Run Online
catch (InterruptedException e) {
    Thread.currentThread().interrupt();
}

Mistake 6: Assuming Multithreading Automatically Makes Programs Faster

More threads do not always mean better performance.

Too many threads can introduce:

  • scheduling overhead

  • context switching

  • contention

  • synchronization overhead

  • increased memory usage

The appropriate concurrency design depends on the workload.


Thread vs Runnable

FeatureExtending ThreadImplementing Runnable
Defines taskYesYes
Extends a classYesNo
Separates task from threadLessBetter
Can extend another classNoYes
Common traditional approachYesYes
Flexible designLowerHigher

For many traditional examples, Runnable is a better separation of concerns.


Modern Java: ExecutorService

Creating raw threads manually is useful for learning, but production applications often use higher-level concurrency abstractions.

For example:

Main.java
Run Online
ExecutorService executor = Executors.newFixedThreadPool(3);

Then submit tasks:

Main.java
Run Online
executor.submit(() -> System.out.println("Task running"));

Finally:

An executor manages a pool of worker threads and lets your application submit tasks without manually creating a new Thread for every job.

This is a major topic in Java concurrency and should be learned after understanding the basic thread model.


Why Thread Pools Are Useful

Imagine processing 10,000 tasks.

Creating 10,000 independent threads is generally not a good design.

A thread pool can instead maintain a controlled number of worker threads:

Architecture Diagram
Task Queue
   |
   +── Task 1
   +── Task 2
   +── Task 3
   +── Task 4
   +── ...
        |
↓
Thread Pool
   ┌───────────┐
   │ Worker 1  │
   │ Worker 2  │
   │ Worker 3  │
   └───────────┘

Tasks can be executed by available workers.


Virtual Threads

Modern Java also provides virtual threads, which are lightweight threads managed by the Java runtime.

They are especially useful for applications with large numbers of tasks that spend significant time waiting on blocking operations.

A basic example in modern Java is:

Main.java
Run Online
Thread.startVirtualThread(() -> {
    System.out.println("Running in a virtual thread");
});

Virtual threads do not replace every use case for platform threads. CPU-intensive work still depends on available CPU resources, while virtual threads are particularly valuable for high-concurrency workloads involving blocking operations.


Platform Threads vs Virtual Threads

A simplified comparison:

Platform ThreadsVirtual Threads
Backed by OS threadsManaged by the Java runtime
More resource-intensiveMuch lighter
Suitable for many traditional workloadsExcellent for high-concurrency blocking workloads
Limited by OS-thread resourcesCan support very large numbers of concurrent tasks

Virtual threads are an important modern Java concurrency feature.


Real-World Multithreading Examples

Web Server

A server may process multiple client requests concurrently:

Main.java
Run Online
Request 1 → Thread ARequest 2 → Thread BRequest 3 → Thread C

File Processing

Different independent files can be processed concurrently:

Main.java
Run Online
File A → Worker 1File B → Worker 2File C → Worker 3

Background Processing

A web application can perform background work without blocking the main request-handling path.

Network Operations

Multiple independent network operations can overlap while waiting for responses.

Data Processing

Large workloads can sometimes be divided into independent tasks.


A Simple Real-World Example

Imagine an online shopping application.

A request might involve:

Architecture Diagram
Order Request
     |
     +── Validate Order
     |
     +── Update Inventory
     |
     +── Send Notification
     |
     +── Record Analytics

Some operations may be suitable for asynchronous or concurrent processing depending on application requirements.

The goal is not simply to create more threads. The goal is to design tasks so that concurrency improves responsiveness, throughput, or resource utilization without creating correctness problems.


Best Practices for Java Multithreading

1. Prefer tasks over manually managing many threads

Use Runnable, Callable, executor services, or higher-level concurrency APIs where appropriate.

2. Keep shared mutable state limited

The less shared mutable state you have, the fewer synchronization problems you usually face.

3. Do not depend on accidental thread ordering

If order matters, establish it explicitly.

4. Handle interruption properly

Treat interruption as a cooperative cancellation signal.

5. Avoid unnecessary thread creation

Thread pools and virtual threads can be more appropriate depending on the workload.

6. Use synchronization deliberately

Synchronization is important when shared state requires coordination, but excessive locking can reduce scalability.

7. Prefer higher-level concurrency utilities

Java provides tools such as:

Main.java
Run Online
ExecutorServiceFutureCompletableFutureConcurrentHashMapBlockingQueueCountDownLatchSemaphore

These can solve common concurrency problems more safely than manually coordinating raw threads.


Complete Multithreading Example

Main.java
Run Online
public class Main {
    public static void main(String[] args) throws InterruptedException {
        Thread worker1 = new Thread(() -> {
            for (int i = 1;
            i <= 5;
            i++) {
                System.out.println( Thread.currentThread().getName() + " → " + i);
            }
        }, "Worker-1");
        Thread worker2 = new Thread(() -> {
            for (int i = 1;
            i <= 5;
            i++) {
                System.out.println( Thread.currentThread().getName() + " → " + i);
            }
        }, "Worker-2");
        worker1.start();
        worker2.start();
        worker1.join();
        worker2.join();
        System.out.println("All work completed");
    }
}

Possible output:

Main.java
Run Online
Worker-1 → 1Worker-2 → 1Worker-1 → 2Worker-2 → 2Worker-2 → 3Worker-1 → 3...All work completed

The exact order of the worker output can vary.

The important guarantee here is that:

Main.java
Run Online
worker1.join();
worker2.join();

causes the main thread to wait until both worker threads have completed before printing:


Frequently Asked Questions

What is multithreading in Java?

Multithreading is the execution of multiple threads within a Java process, allowing different tasks to make progress concurrently.

What is a thread?

A thread is an independent path of execution within a process.

What is the main thread?

The main thread is the thread that begins execution of a Java application's main() method.

How do you create a thread in Java?

Traditional approaches include extending Thread or implementing Runnable. Modern applications also commonly use executor frameworks and virtual threads.

What is the difference between start() and run()?

start() initiates a new thread of execution. Calling run() directly is just a normal method call on the current thread.

Can a Java thread be started twice?

No. A Thread object can be started only once.

What are the Java thread states?

The official states are:

Main.java
Run Online
NEWRUNNABLEBLOCKEDWAITINGTIMED_WAITINGTERMINATED

What does sleep() do?

Thread.sleep() pauses the current thread for approximately the specified duration.

Does sleep() release a lock?

No. Sleeping does not automatically release monitors held by the thread.

What does join() do?

join() causes the current thread to wait for another thread to terminate.

What is a daemon thread?

A daemon thread is a background thread that does not keep the JVM alive by itself after all non-daemon threads have terminated.

What is ConcurrentModificationException?

It is commonly associated with fail-fast detection of certain structural modifications made to a collection while it is being iterated.

Does multithreading always improve performance?

No. Thread creation, scheduling, synchronization, contention, and context switching can introduce overhead.

What is a virtual thread?

A virtual thread is a lightweight Java thread managed by the Java runtime and designed to support very high levels of concurrency, particularly for blocking workloads.

Graphics and Design Ideas

Graphic 1: Java Thread Lifecycle

Create a clean lifecycle diagram:

Architecture Diagram
              ┌──────────┐
              │   NEW    │
              └────┬─────┘
                   │ start()
↓
┌───────────┐
             │ RUNNABLE  │
             └─────┬─────┘
              ↙
↓
↘
        BLOCKED  WAITING  TIMED
              ↘
↓
↙
             ┌───────────┐
             │ RUNNABLE  │
             └─────┬─────┘
                   │
↓
┌─────────────┐
            │ TERMINATED  │
            └─────────────┘

This should be the main visual for the page.

Graphic 2: Process vs Threads

Architecture Diagram
             Java Process
                  |
       ┌──────────┼──────────┐
↓

↓

↓
Thread 1   Thread 2   Thread 3
       |          |          |
      Task       Task       Task

Use different icons for the process and individual threads.

Graphic 3: start() vs run()

Main.java
Run Online
thread.start()↓New Thread↓run()↓Task executes

Compared with:

Main.java
Run Online
thread.run()↓Normal method call↓Current thread executes

Graphic 4: Concurrency vs Parallelism

Main.java
Run Online
ConcurrencyCPU↓A → B → A → BParallelismCPU 1 → Task ACPU 2 → Task B

This visual can make the distinction easy for beginners.

Graphic 5: Thread Pool

Architecture Diagram
             Task Queue
          ┌──┬──┬──┬──┐
↓

↓

↓

↓

↓
┌──────────────────┐
       │   Thread Pool    │
       │ Worker 1         │
       │ Worker 2         │
       │ Worker 3         │
       └──────────────────┘

This provides a natural introduction to the next topic, ExecutorService.

Runnable Code Example: Java Multithreading

Java 17+
Main.java
Run Online
public class ThreadLifecycleDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread worker = new Thread(() -> {
            try {
                Thread.sleep(200);
                System.out.println("Worker completed task on thread: " + Thread.currentThread().getName());
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
        System.out.println("State before start: " + worker.getState());
        // NEW
        worker.start();
        System.out.println("State after start:  " + worker.getState());
        // RUNNABLE
        worker.join();
        System.out.println("State after completion: " + worker.getState());
        // TERMINATED
    }
}

Common Java Technical Interview Questions & Answers

Interview Prep
Q1

What are the 6 states of a thread in Java?

NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, and TERMINATED.
Q2

What is a daemon thread in Java?

A background thread (set via setDaemon(true)) that does not prevent the JVM from exiting when all user threads finish.

Key Takeaways, Best Practices & Clean Code Rules

Java Best Practice & Production Rules

Prefer thread pools via ExecutorService or Virtual Threads (Java 21+) over manually creating and starting raw Thread objects.