Skip to content

Threads and Runnable — Thread Class, Runnable, Callable, Thread States, and Daemon Threads

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about Threads and Runnable. We cover key concepts, practical examples, and best practices to help you master this topic.

Java threads enable concurrent execution within a single Process, with the Thread class and Runnable interface providing the foundation. A thread is the smallest unit of execution — multiple threads share the same memory space but execute independently, making them powerful but also dangerous when accessing shared data.

What You'll Learn

  • Creating threads with Thread class and Runnable
  • Thread states: NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED
  • Callable for returning values and throwing exceptions
  • Daemon vs user threads

Why It Matters

Understanding threads is essential for building responsive applications. A GUI that freezes during file loading is using threads incorrectly. A server that cannot handle multiple clients is using threads incorrectly. Threads are the foundation of all concurrency.

Real-World Use

Web servers use thread pools to handle requests. GUIs use a separate thread for background tasks. Batch processors parallelize work across threads.


Creating Threads

Extending Thread

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

MyThread t = new MyThread();
t.start();  // starts a new thread
// Never call run() directly — that runs in the calling thread

Implementing Runnable

class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Runnable running in: " + Thread.currentThread().getName());
    }
}

Thread t = new Thread(new MyRunnable());
t.start();

Lambda Syntax

Thread t = new Thread(() -> {
    System.out.println("Lambda thread running");
});
t.start();

The Thread Lifecycle

A thread goes through six states:

  1. NEW — created with new Thread() but not started
  2. RUNNABLE — after start(), eligible for CPU scheduling
  3. BLOCKED — waiting to acquire a monitor lock
  4. WAITING — waiting indefinitely for another thread (wait/join/park)
  5. TIMED_WAITING — waiting for a specified time (sleep/wait(timeout)/join(timeout))
  6. TERMINATED — execution completed
Thread t = new Thread(() -> {
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
});

System.out.println(t.getState()); // NEW
t.start();
System.out.println(t.getState()); // RUNNABLE
Thread.sleep(100);
System.out.println(t.getState()); // TIMED_WAITING (probably)
t.join();
System.out.println(t.getState()); // TERMINATED

Callable and Future

Runnable cannot return a value or throw checked exceptions. Callable fixes both:

Callable<Integer> task = () -> {
    Thread.sleep(1000);
    return 42;
};

ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(task);

// Get the result (blocks until done)
Integer result = future.get();
System.out.println(result); // 42

executor.shutdown();

Future Methods

future.get();                    // blocks until done
future.get(1, TimeUnit.SECONDS); // blocks with timeout
future.isDone();                 // non-blocking check
future.cancel(true);            // attempt to cancel

Daemon Threads

Daemon threads run in the background and do not prevent the JVM from exiting:

Thread daemon = new Thread(() -> {
    while (true) {
        System.out.println("Daemon running...");
        try { Thread.sleep(1000); } catch (InterruptedException e) { break; }
    }
});
daemon.setDaemon(true);
daemon.start();

// JVM exits after main thread finishes, even if daemon is still running

Use cases: background Garbage Collection, monitoring, auto-save.

Thread Methods

Thread t = new Thread(task);

t.start();           // begin execution
t.join();            // wait for thread to finish
t.join(1000);        // wait with timeout
t.setName("worker"); // name for debugging
t.setPriority(Thread.MAX_PRIORITY); // not reliable across platforms
t.interrupt();       // request interruption

Thread.sleep(1000);  // static: current thread sleeps
Thread.yield();      // static: hint to scheduler

Common Mistakes

  1. Calling run() instead of start(). This runs the method in the current thread, not a new thread.
  2. Starting a thread twice. Once a thread is started and terminated, it cannot be restarted.
  3. Using Thread.stop(). It is deprecated because it releases all monitors, potentially leaving shared data in an inconsistent state.
  4. Ignoring InterruptedException. Catching and ignoring it swallows the interruption request. Restore the interrupt flag: Thread.currentThread().interrupt().
  5. Using threads directly instead of thread pools. Creating threads is expensive. Use ExecutorService for production code.

Practice Questions

1. What is the difference between start() and run() on a Thread?
start() creates a new thread and calls run() in that thread. run() executes in the calling thread — no new thread is created.

2. What are the six thread states?
NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED.

3. What is the difference between Runnable and Callable?
Runnable returns void and cannot throw checked exceptions. Callable returns a value and can throw checked exceptions.

4. What is a daemon thread?
A background thread that does not prevent the JVM from exiting. The JVM shuts down when only daemon threads remain.

5. Why is Thread.stop() deprecated?
It releases all monitors, leaving shared data in an inconsistent state. Use interrupt() instead.

Challenge Question:
Write a program that calculates the sum of a large array (10 million integers) using multiple threads. Divide the array into equal parts, create a Callable for each part, submit to an ExecutorService, collect Futures, and sum the results. Compare the time with a single-threaded version.

FAQ

What happens if I call `start()` twice on the same thread?

It throws IllegalThreadStateException. A thread can only be started once.

What is the difference between `sleep()` and `wait()`?

sleep() pauses the current thread without releasing any locks. wait() releases the monitor lock and waits for notification. Both put the thread in TIMED_WAITING or WAITING state.

What is a thread interruption?

A cooperative mechanism where one thread asks another to stop. The interrupted thread checks Thread.interrupted() or catches InterruptedException. It is up to the interrupted thread to decide how to respond.

What is the default stack size for a thread?

Platform-dependent (typically 1 MB on 64-bit Linux). You can configure it with the -Xss JVM flag.

How many threads can a JVM support?

Limited by OS resources (memory, thread limit). On Linux, the limit is typically in the thousands. Beyond that, consider virtual threads (Project Loom, Java 21+).

Mini Project

Write a program ThreadDemo.java that:

  1. Creates 5 threads using different approaches (extends Thread, implements Runnable, lambda, anonymous class)
  2. Each thread prints its name, priority, and state
  3. Uses join() to wait for all threads to finish
  4. Creates a daemon thread that runs in the background
  5. Uses Callable and Future to compute a value
  6. Demonstrates thread states by printing state transitions
  7. Shows proper interruption handling

What's Next

Multiple threads accessing shared data need coordination. Lesson 52 covers synchronization — the synchronized keyword, volatile, atomic classes, and the happens-before relationship.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro