Multithreading Example in Java

In this post, we implement a basic Multithreading example in Java. Multithreading allows multiple threads to execute concurrently within a single program, enabling tasks to run in parallel rather than one after another. Java has built-in support for multithreading through the Thread class and the Runnable interface.

What is Multithreading?

A thread is the smallest unit of execution within a process. When a program creates multiple threads, the operating system’s thread scheduler interleaves their execution — giving each thread a slice of CPU time in turn. This makes it appear as though they are running simultaneously (and on multi-core systems, they actually can be).

In this example, we create two threads by extending the Thread class and overriding its run() method. Each thread prints a label 4 times, pausing 500 ms between prints. Both threads run concurrently, so their output interleaves.

  • start() — Tells the JVM to create a new OS-level thread and invoke run() on it. Calling run() directly would execute it on the current thread, not a new one.
  • Thread.sleep(ms) — Pauses the current thread for the given number of milliseconds, allowing other threads to execute.
  • InterruptedException — Must be caught when calling sleep(). It fires if another thread interrupts this one while it is sleeping.

Java Code Implementation

// ============================================================
// Multithreading Example in Java
// Demonstrates concurrent execution of two threads
// Each thread prints a label 4 times with a 500ms pause
// ============================================================

// Custom thread class — extends the built-in Thread class
class LabelPrinterThread extends Thread {

    private String threadLabel;   // The text this thread will print on each iteration

    // Constructor: assigns the display label to this thread instance
    LabelPrinterThread(String label) {
        this.threadLabel = label;
    }

    // run() is called automatically by the JVM when start() is invoked.
    // This method contains all the work this thread will perform.
    @Override
    public void run() {
        for (int count = 0; count < 4; count++) {      // Print label exactly 4 times
            System.out.println("  " + threadLabel);
            try {
                Thread.sleep(500);                      // Pause 500 ms before next print
            } catch (InterruptedException interruptedEx) {
                // Handle the case where this thread is interrupted during sleep
                System.out.println(threadLabel + " thread was interrupted: "
                                   + interruptedEx.getMessage());
            }
        }
    }
}

// Main class — entry point of the program
public class MultithreadingDemo {

    public static void main(String[] args) {

        // Create two thread instances, each with a different label
        LabelPrinterThread thread1 = new LabelPrinterThread("piit");
        LabelPrinterThread thread2 = new LabelPrinterThread("new panvel");

        // Start both threads — the JVM schedules them for concurrent execution.
        // The order of their output is determined by the OS thread scheduler
        // and is NOT guaranteed to be strictly alternating.
        thread1.start();
        thread2.start();

        // main() may return here before the threads finish — that is expected.
        // The JVM keeps running until all non-daemon threads complete.
    }
}

Explanation of the Code

  1. LabelPrinterThread extends Thread — Creates a custom thread type. The field threadLabel stores the string each instance will print. By extending Thread, the class inherits start(), sleep(), and other threading infrastructure.
  2. Constructor — Accepts the label string and stores it as an instance variable. Each thread object maintains its own copy of threadLabel, so the two threads are independent.
  3. run() method — The @Override annotation confirms we are correctly overriding Thread.run(). The loop runs 4 times. Inside, Thread.sleep(500) voluntarily yields CPU time for 500 ms, giving the other thread a chance to run.
  4. thread1.start() vs thread1.run() — Calling start() creates a new OS thread and invokes run() on it asynchronously. Calling run() directly would execute it synchronously on the main thread — no concurrency. Always use start() for true multithreading.

Sample Output

  piit
  new panvel
  piit
  new panvel
  new panvel
  piit
  piit
  new panvel

Step-by-Step Explanation of Input/Output

This program takes no user input. Both threads are created and started programmatically in main().

  • After thread1.start() and thread2.start(), both threads are alive and competing for CPU time.
  • Thread 1 prints "piit" and then sleeps 500 ms. During that sleep, Thread 2 gets the CPU and prints "new panvel", then also sleeps.
  • This alternating pattern repeats roughly 4 times each.
  • The output order is not guaranteed. Sometimes two consecutive lines from the same thread appear (as shown on lines 5–6 where "new panvel" then "piit" appear back-to-back). This depends on OS scheduling decisions and can vary between runs and machines.
  • Total output: 4 prints from Thread 1 + 4 prints from Thread 2 = 8 lines.

See Also

Bottom line is…

Extending Thread is the simplest way to create threads in Java, but it has a limitation: since Java supports only single inheritance, your class can’t extend any other class if it already extends Thread. The preferred approach in real-world code is to implement the Runnable interface instead. For production systems, Java’s ExecutorService and java.util.concurrent package provide higher-level thread management with thread pools, futures, and task queuing — far more powerful than managing raw threads manually.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.