Chaturmind
LearnDSASystem DesignDevOpsEngineering GrowthBlog
Start learning
Chaturmind

Structured learning paths for engineers who want to go deep. Written by practitioners.

Learn

  • Java
  • DSA
  • System Design
  • Spring Boot
  • AI / ML
  • DevOps
  • Engineering Growth

Company

  • Blog
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Chaturmind. All rights reserved.

Built for engineers who want to go deep.


← Java Concurrency & Multithreading

Threads & Runnable

  • Introduction to Threads
  • ExecutorService & Thread Pools
  • Fork/Join Framework

Synchronization & Memory Model

  • synchronized and Locks
  • volatile and the Memory Model
  • CompletableFuture
  • Deadlock, Starvation, Livelock

Concurrent Collections

  • ConcurrentHashMap & CopyOnWriteArrayList
  • HashMap Concurrency Variants
  • Concurrent Utilities & Coordination
Chaturmind
← Java Concurrency & Multithreading

Threads & Runnable

  • Introduction to Threads
  • ExecutorService & Thread Pools
  • Fork/Join Framework

Synchronization & Memory Model

  • synchronized and Locks
  • volatile and the Memory Model
  • CompletableFuture
  • Deadlock, Starvation, Livelock

Concurrent Collections

  • ConcurrentHashMap & CopyOnWriteArrayList
  • HashMap Concurrency Variants
  • Concurrent Utilities & Coordination
HomeLearnJavaJava Concurrency & MultithreadingThreads & Runnable
✓ FreeIntermediate· 10 min read

Introduction to Threads

Create and start threads — Thread class, Runnable, and the lifecycle of a thread.

Published September 21, 2026


Introduction to Java Threads

A thread is the smallest unit of execution within a process. Java supports multithreading natively, allowing multiple threads to run concurrently within the same JVM process, sharing heap memory.

Creating Threads

Option 1: Extend Thread

public class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Running in: " + Thread.currentThread().getName());
    }
}

new MyThread().start(); // start() creates a new OS thread and calls run()

Option 2: Implement Runnable (preferred)

Runnable task = () -> System.out.println("Task in: " + Thread.currentThread().getName());

Thread t = new Thread(task, "worker-1");
t.start();

Prefer Runnable over extending Thread — it separates the task from the thread mechanism, and a class can only extend one class.

Thread Lifecycle

NEW → RUNNABLE → [RUNNING] → TERMINATED
              ↕
         BLOCKED/WAITING/TIMED_WAITING
  • NEW: Thread created but not started
  • RUNNABLE: Ready to run or running (JVM scheduler decides)
  • BLOCKED: Waiting for a monitor lock (e.g., synchronized)
  • WAITING: Waiting indefinitely (Object.wait(), Thread.join())
  • TIMED_WAITING: Waiting with timeout (Thread.sleep(ms), LockSupport.parkNanos())
  • TERMINATED: run() completed or threw an exception
Thread t = new Thread(() -> { /* ... */ });
System.out.println(t.getState()); // NEW
t.start();
System.out.println(t.getState()); // RUNNABLE
t.join();                          // wait for completion
System.out.println(t.getState()); // TERMINATED

Key Thread Methods

Thread t = new Thread(task);
t.setName("processor-1");  // useful for debugging
t.setDaemon(true);         // JVM exits even if daemon threads are running
t.setPriority(Thread.MAX_PRIORITY); // 1-10, default 5 (hint only)
t.start();                 // begin execution
t.join();                  // wait for this thread to finish
t.join(5000);              // wait max 5 seconds
t.interrupt();             // request interruption

// Check interruption
if (Thread.currentThread().isInterrupted()) {
    // clean up and stop
}

// Static methods
Thread.sleep(1000);        // pause current thread (throws InterruptedException)
Thread.yield();            // hint to scheduler to yield CPU
Thread.currentThread();    // reference to currently running thread

Handling InterruptedException

public void run() {
    try {
        Thread.sleep(10000);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt(); // IMPORTANT: restore interrupt flag
        return; // exit cleanly
    }
}

Never swallow InterruptedException silently — always restore the interrupt flag or re-throw.

Race Conditions

// UNSAFE: counter++ is not atomic (read-modify-write)
private int counter = 0;
public void increment() { counter++; } // race condition!

// SAFE: use AtomicInteger
private AtomicInteger counter = new AtomicInteger();
public void increment() { counter.incrementAndGet(); }

Runnable vs Callable

Runnable's single method is void run() — no return value, and it cannot throw a checked exception. Callable<V>'s single method is V call() throws Exception — it returns a value and is allowed to throw a checked exception, which is exactly why ExecutorService.submit() accepts a Callable and hands back a Future<V> you can call .get() on, while execute() only accepts a Runnable and gives nothing back. Reach for Callable any time the task produces a result or can fail with a checked exception; Runnable is for pure side-effecting, non-failing work.

wait() vs sleep() — the distinction that causes real deadlocks

Both pause a thread, but they differ in exactly the way that matters under load: wait() (defined on Object, must be called from inside a synchronized block) releases the monitor lock it's called with while paused, letting other threads acquire that same lock and make progress. Thread.sleep() holds onto any locks the thread currently owns for the entire sleep duration — it has no awareness of locks at all, it just pauses the thread.

This difference has a concrete failure mode: a thread that calls Thread.sleep() while holding a lock blocks every other thread waiting on that same lock for the full sleep duration — a self-inflicted contention (or outright deadlock, if another thread is waiting on this one to release the lock before it can do the same) that's easy to introduce by reaching for sleep() inside a synchronized block when wait()/a Condition was the correct tool.

Interview Tips

  1. Know the difference between start() (creates new thread) and run() (executes in the calling thread — does NOT create a new thread).
  2. Daemon threads vs user threads: JVM waits for user threads to finish but not daemon threads. Background tasks (GC, timers) use daemon threads.
  3. Thread.sleep() does NOT release locks — Object.wait() does.

Next

ExecutorService & Thread Pools

AI Tutor

Lesson: Introduction to Threads

Quick actions

AI responses can be inaccurate. Verify critical information.