› Module 11: Multithreading › 11.1 Creating Threads: Thread and Runnable
M O D U L E

Module 11: Multithreading

Concurrent execution via Thread and Runnable, thread lifecycle states, race conditions, and monitor synchronization.

11.1 Creating Threads: Thread and Runnable

A Thread is an independent path of execution running concurrently within a single program process, sharing heap memory while maintaining its own dedicated stack space.

Two Mechanisms to Create Threads

  • 1. Extending Thread: Create a subclass of Thread and override its run() method. (Disadvantage: consumes the class's single inheritance slot).
  • 2. Implementing Runnable (Best Practice): Pass a Runnable functional interface instance into a Thread constructor. Keeps inheritance open for domain models:
    Runnable task = () -> System.out.println("Running in a new thread: " + Thread.currentThread().getName());
    Thread t = new Thread(task);
    t.start(); // Spawns a new concurrent OS thread!
Critical Beginner Pitfall:

Always call .start(), NEVER .run() directly! Calling .run() executes the code synchronously on the current caller thread like a normal method. Calling .start() signals the JVM to allocate a new call stack and register a concurrent thread with the OS scheduler.

New Runnable Running Terminated Blocked / Waiting
Figure 11.1 — A thread's lifecycle: New → Runnable → Running → (Blocked) → Terminated.

11.2 Synchronization

When multiple concurrent threads read and write shared mutable state without coordination, a race condition occurs — producing corrupted or unpredictable data depending on arbitrary CPU scheduling timing.

The synchronized Keyword

Every Java object contains an intrinsic Monitor Lock. Applying synchronized guarantees mutual exclusion (Mutex): only one thread can hold the lock and execute the protected block or method at any given moment. Other threads must wait their turn in the Blocked state:

class BankAccount {
    private int balance = 1000;

    // Synchronized method locks on the current instance (this)
    public synchronized void withdraw(int amount) {
        if (balance >= amount) {
            balance -= amount;
        }
    }
}
KEY TERM: Race condition

An unpredictable bug that occurs when multiple threads access shared data concurrently without synchronization, and the outcome depends on the unpredictable timing of which thread happens to run first.

💡 Placement & Interview Deep-Dive

  • What is the difference between wait() and Thread.sleep()?
    • wait() is a method of java.lang.Object. It releases the monitor lock, allowing other waiting threads to enter the synchronized block, and waits for notify()/notifyAll().
    • Thread.sleep() is a static method of Thread. It pauses execution for a specified duration WITHOUT releasing any held monitor locks.
  • What does the volatile keyword do? Marking a variable volatile guarantees Memory Visibility across threads: reads and writes bypass local CPU L1/L2 caches and write directly to main RAM, establishing a happens-before relationship. It also prevents CPU instruction reordering. Note: volatile does NOT guarantee atomicity for compound operations like count++!
  • What is a Deadlock? A situation where two or more threads are permanently blocked, each holding a lock that the other needs (e.g. Thread 1 holds Lock A waiting for Lock B; Thread 2 holds Lock B waiting for Lock A).
MODULE COMPREHENSIVE QUIZ

Module 11 Final Quiz

Test your understanding across all 2 topics of this module with interview-grade questions, tricky edge cases, and code output predictions.

Take Module 11 Final Quiz →
Yukti Mascot