← Back to Home

Creating Threads

In Java, threads enable concurrent execution within a single process. Java provides two primary ways to create threads, plus modern executor-based approaches used in production systems. This is a high-frequency interview topic.

Ways to Create Threads in Java

  1. Extend Thread class
  2. Implement Runnable interface
  3. Implement Callable (Executor framework) (advanced/modern)

1️⃣ Creating a Thread by Extending Thread Class

How It Works

  • Create a subclass of Thread
  • Override the run() method
  • Call start() to begin execution

Example

class MyThread extends Thread {

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

public class Test {
    public static void main(String[] args) {
        MyThread t = new MyThread();
        t.start(); // starts new thread
    }
}
          

Important Rules

  • run() contains the thread logic
  • start() creates a new call stack
  • Calling run() directly ❌ does NOT create a new thread

Limitations

  • Java does not support multiple inheritance
  • Class cannot extend any other class

2️⃣ Creating a Thread by Implementing Runnable (Most Preferred)

How It Works

  • Implement Runnable
  • Pass object to Thread constructor
  • Call start()

Example

class MyTask implements Runnable {

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

public class Test {
    public static void main(String[] args) {
        Thread t = new Thread(new MyTask());
        t.start();
    }
}
          

Why Runnable Is Preferred (Interview Point)

  • Supports multiple inheritance
  • Separates task from thread
  • Better object-oriented design
  • Widely used in frameworks

3️⃣ Creating Thread Using Lambda (Java 8+)

Thread t = new Thread(() -> {
    System.out.println("Thread using lambda");
});
t.start();
          
  • ✔ Clean
  • ✔ Modern
  • ✔ Uses Runnable internally

4️⃣ Creating Threads Using Callable (Executor Framework)

Used when:

  • Thread must return a value
  • Thread may throw checked exceptions
Callable task = () -> 10 + 20;

ExecutorService service = Executors.newSingleThreadExecutor();
Future result = service.submit(task);

System.out.println(result.get());
service.shutdown();
          
  • ✔ Production-ready
  • ✔ Controlled thread management

start() vs run() (Very Important)

Method Behavior
start() Creates new thread
run() Executes like normal method
t.run();   // ❌ no new thread
t.start(); // ✔ new thread
          

Thread Naming (Good Practice)

Thread t = new Thread(new MyTask(), "Worker-1");
t.start();
          

Thread Priority (Hint)

t.setPriority(Thread.MAX_PRIORITY);
          

⚠ Priority is scheduler-dependent

Comparison: Thread vs Runnable (Interview Favorite)

Aspect Thread Runnable
Inheritance Extends Thread Implements Runnable
Multiple inheritance ❌ No ✔ Yes
Code separation ❌ Poor ✔ Good
Recommended ❌ Less ✔ More

Best Practices (Real-World)

  • Prefer Runnable / Callable over extending Thread
  • Use ExecutorService for thread management
  • Avoid manual thread creation in large systems
  • Name threads meaningfully
  • Do not override start()

Common Beginner Mistakes

  • Calling run() instead of start()
  • Extending Thread unnecessarily
  • Creating too many threads
  • Ignoring thread lifecycle
  • Not shutting down executors

Interview-Ready Answers

Short Answer

Threads can be created by extending the Thread class or implementing Runnable.

Detailed Answer

In Java, threads can be created by extending the Thread class or implementing the Runnable interface. Implementing Runnable is preferred because it supports multiple inheritance and separates task logic from thread management. Modern applications use the Executor framework with Runnable or Callable for better scalability.

Key Takeaway

Runnable for design.
Thread for execution.
Executor for production.
Mastering thread creation is the foundation for safe, scalable, and efficient Java concurrency.

Practical Thread Creation Examples

1. Creating Thread by Extending Thread

class MyThread extends Thread {
    public void run() {
        System.out.println("Thread using Thread class");
    }

    public static void main(String[] args) {
        MyThread t = new MyThread();
        t.start();
    }
}
          

2. Creating Thread Using Runnable (Preferred)

class MyTask implements Runnable {
    public void run() {
        System.out.println("Thread using Runnable");
    }

    public static void main(String[] args) {
        Thread t = new Thread(new MyTask());
        t.start();
    }
}
          

3. Thread Using Lambda Expression

class Demo {
    public static void main(String[] args) {
        Thread t = new Thread(() ->
                System.out.println("Thread using Lambda"));
        t.start();
    }
}
          

4. Anonymous Runnable Implementation

class Demo {
    public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            public void run() {
                System.out.println("Anonymous Runnable");
            }
        });
        t.start();
    }
}
          

5. Multiple Threads Creation

class Demo {
    public static void main(String[] args) {
        new Thread(() -> System.out.println("Thread 1")).start();
        new Thread(() -> System.out.println("Thread 2")).start();
    }
}
          

6. Naming a Thread

class Demo {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            System.out.println(Thread.currentThread().getName());
        }, "Worker-1");
        t.start();
    }
}
          

7. Getting Current Thread

class Demo {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName());
    }
}
          

Output

main
          

8. Creating Thread Using ExecutorService (Industry Standard)

import java.util.concurrent.*;

class Demo {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        executor.execute(() -> System.out.println("Executor Thread"));
        executor.shutdown();
    }
}
          

9. Fixed Thread Pool

import java.util.concurrent.*;

class Demo {
    public static void main(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(2);
        pool.execute(() -> System.out.println("Task 1"));
        pool.execute(() -> System.out.println("Task 2"));
        pool.shutdown();
    }
}
          

10. Creating Thread Using Callable (Returns Value)

import java.util.concurrent.*;

class Demo {
    public static void main(String[] args) throws Exception {
        Callable task = () -> 10 + 20;
        ExecutorService ex = Executors.newSingleThreadExecutor();
        Future result = ex.submit(task);
        System.out.println(result.get());
        ex.shutdown();
    }
}
          

Output

30
          

11. Difference: run() vs start()

class Demo {
    public static void main(String[] args) {
        Thread t = new Thread(() -> System.out.println("Thread"));
        t.run(); // ❌ no new thread
    }
}
          

Explanation

  • run() → normal method
  • start() → new thread

12. Thread with Sleep

class Demo {
    public static void main(String[] args) {
        new Thread(() -> {
            try {
                Thread.sleep(500);
                System.out.println("After sleep");
            } catch (Exception e) {}
        }).start();
    }
}
          

13. Thread with Join

class Demo {
    public static void main(String[] args) throws Exception {
        Thread t = new Thread(() -> {
            try { Thread.sleep(300); } catch (Exception e) {}
            System.out.println("Worker done");
        });

        t.start();
        t.join();
        System.out.println("Main resumed");
    }
}
          

14. Daemon Thread Creation

class Demo {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            while (true) {}
        });
        t.setDaemon(true);
        t.start();
        System.out.println("Main ends");
    }
}
          

15. Thread Priority (Hint to Scheduler)

class Demo {
    public static void main(String[] args) {
        Thread t = new Thread(() -> System.out.println("Priority"));
        t.setPriority(Thread.MAX_PRIORITY);
        t.start();
    }
}
          

16. Creating Thread Using Method Reference

class Demo {
    static void task() {
        System.out.println("Method Reference Thread");
    }

    public static void main(String[] args) {
        Thread t = new Thread(Demo::task);
        t.start();
    }
}
          

17. Thread Factory (Advanced)

import java.util.concurrent.*;

class Demo {
    public static void main(String[] args) {
        ThreadFactory factory = r -> new Thread(r, "Custom-Thread");
        ExecutorService ex = Executors.newSingleThreadExecutor(factory);
        ex.execute(() -> System.out.println(Thread.currentThread().getName()));
        ex.shutdown();
    }
}
          

18. Creating Thread Inside Loop

class Demo {
    public static void main(String[] args) {
        for (int i = 1; i <= 3; i++) {
            int id = i;
            new Thread(() -> System.out.println("Thread " + id)).start();
        }
    }
}
          

19. Thread with Exception Handling

class Demo {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            throw new RuntimeException("Error");
        });
        t.setUncaughtExceptionHandler(
            (th, ex) -> System.out.println("Handled: " + ex.getMessage()));
        t.start();
    }
}
          

20. Interview Summary – Creating Threads

  • Thread
  • Runnable
  • Lambda
  • ExecutorService
  • Callable

Key Points

  • Prefer Runnable / ExecutorService
  • start() always for new thread
  • Callable returns value
  • Executor manages threads efficiently
  • Daemon threads don’t block JVM exit