Advanced Topics

Java Multithreading

Learn multithreading in Java — creating threads, synchronization, thread lifecycle, and concurrent programming basics.

What is Multithreading?

Multithreading allows a program to execute multiple threads simultaneously, improving performance and responsiveness. Each thread is a lightweight, independent path of execution.

Creating Threads

Method 1: Extending Thread Class

java
public class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println(Thread.currentThread().getName() + ": " + i);
            try { Thread.sleep(500); } catch (InterruptedException e) { }
        }
    }
}

MyThread t1 = new MyThread();
t1.start();  // Starts a new thread

Method 2: Implementing Runnable (Preferred)

java
public class MyTask implements Runnable {
    @Override
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println(Thread.currentThread().getName() + ": " + i);
        }
    }
}

Thread t1 = new Thread(new MyTask(), "Worker-1");
Thread t2 = new Thread(new MyTask(), "Worker-2");
t1.start();
t2.start();

Method 3: Lambda Expression

java
Thread t = new Thread(() -> {
    System.out.println("Running in: " + Thread.currentThread().getName());
});
t.start();

Thread Lifecycle

StateDescription
NewThread created but not started
RunnableReady to run, waiting for CPU
RunningCurrently executing
Blocked/WaitingWaiting for a resource or signal
TerminatedExecution completed

Synchronization

Prevents race conditions when multiple threads access shared data:

java
public class Counter {
    private int count = 0;

    // Synchronized method — only one thread at a time
    public synchronized void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}

Counter counter = new Counter();

Thread t1 = new Thread(() -> {
    for (int i = 0; i < 1000; i++) counter.increment();
});

Thread t2 = new Thread(() -> {
    for (int i = 0; i < 1000; i++) counter.increment();
});

t1.start(); t2.start();
t1.join();  t2.join();

System.out.println(counter.getCount());  // Always 2000

Thread Communication

java
// join() — wait for a thread to complete
t1.start();
t1.join();  // Main thread waits for t1 to finish

// sleep() — pause execution
Thread.sleep(1000);  // Sleep for 1 second

// wait() and notify() — inter-thread communication
synchronized (lock) {
    lock.wait();    // Release lock and wait
    lock.notify();  // Wake up one waiting thread
}

Executor Framework (Modern Approach)

java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

ExecutorService executor = Executors.newFixedThreadPool(3);

for (int i = 1; i <= 5; i++) {
    final int task = i;
    executor.submit(() -> {
        System.out.println("Task " + task + " by " + Thread.currentThread().getName());
    });
}

executor.shutdown();

Practical Example: Parallel Downloader

java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ParallelDownloader {
    static void downloadFile(String filename) {
        System.out.printf("[%s] Downloading %s...%n",
            Thread.currentThread().getName(), filename);
        try { Thread.sleep(1000); } catch (InterruptedException e) { }
        System.out.printf("[%s] Completed %s%n",
            Thread.currentThread().getName(), filename);
    }

    public static void main(String[] args) {
        String[] files = {"report.pdf", "image.png", "data.csv", "video.mp4"};

        ExecutorService pool = Executors.newFixedThreadPool(2);
        for (String file : files) {
            pool.submit(() -> downloadFile(file));
        }
        pool.shutdown();
    }
}

Summary

  • Multithreading runs multiple threads concurrently for better performance
  • Create threads by extending Thread, implementing Runnable, or using lambdas
  • Use synchronized to prevent race conditions on shared data
  • join() waits for thread completion; sleep() pauses execution
  • The Executor Framework is the modern, preferred way to manage threads
  • Use thread pools (ExecutorService) instead of creating threads manually