What are the different ways to create a thread in Java?
Java provides several mechanisms to create and run threads, each with different tradeoffs.
**Extending Thread**: Subclass `java.lang.Thread` and override `run()`. Call `start()` (not `run()` directly — `run()` executes on the current thread; `start()` creates a new OS thread). Drawback: Java's single-inheritance constraint means the class cannot extend anything else.
**Implementing Runnable**: Implement `java.lang.Runnable` (functional interface with one method: `run()`). Pass an instance to `new Thread(runnable).start()`. This separates the task from the thread — you can pass the same Runnable to multiple threads or an executor. This is the preferred approach over extending Thread.
**Implementing Callable**: Like Runnable but `call()` returns a value and can throw a checked exception. Used with `ExecutorService.submit(callable)` which returns a `Future<V>`. Call `future.get()` to block until the result is available.
**ExecutorService**: The modern way. Rather than managing threads manually, submit tasks to a thread pool created by `Executors.newFixedThreadPool(n)`, `Executors.newCachedThreadPool()`, or `Executors.newScheduledThreadPool(n)`. The framework handles thread creation, reuse, and lifecycle. Always call `shutdown()` when done.
**CompletableFuture** (Java 8+): For async, non-blocking composition of tasks. `CompletableFuture.supplyAsync(supplier)` runs a task on the common ForkJoinPool. Supports chaining with `thenApply()`, `thenCompose()`, `exceptionally()`.
**Virtual Threads** (Java 21, Project Loom): `Thread.ofVirtual().start(runnable)` or `Executors.newVirtualThreadPerTaskExecutor()`. Lightweight threads scheduled by the JVM, not the OS. Enable millions of concurrent tasks without the memory cost of OS threads.
For most modern code, `ExecutorService` with `Callable`/`Runnable` is the standard. Avoid extending Thread or creating bare threads in production code.
Covers at least three approaches correctly and explains why start() must be used instead of run().
Also covers ExecutorService as the production standard, the difference between Runnable and Callable, Future for result retrieval, and optionally Virtual Threads.
Reading the answer is step one. Explaining it unprompted — under interview pressure — is what actually matters. Get AI-graded feedback on your answer with follow-up probes on your weak points.
Get Graded — Free Assessment