Difficulty: Medium
Correct Answer: You can either extend the Thread class or implement the Runnable interface, and in most cases implementing Runnable is preferred because it separates the task from the thread and allows the class to extend another superclass
Explanation:
Introduction / Context:
This question deals with basic Java concurrency and the classic interview topic of how to create and start a thread. Java provides two traditional approaches: extending the Thread class or implementing the Runnable interface. Modern code often uses executors and higher level abstractions, but understanding these foundational mechanisms is important for interviews and for grasping how the threading model works under the hood.
Given Data / Assumptions:
Concept / Approach:
The first approach is to create a subclass of Thread and override the run() method with the code that should execute in the new thread. The second approach is to implement the Runnable interface in a separate class or as a lambda expression and then pass that Runnable to a Thread object. In both cases, calling start() on the Thread object causes the virtual machine to create a new thread of execution that runs the run() method. Implementing Runnable is generally preferred because it promotes separation of concerns: Runnable encapsulates the task, while Thread represents the execution mechanism. It also avoids the limitation of single inheritance in Java, since a class that extends Thread cannot extend any other class.
Step-by-Step Solution:
Step 1: Approach one: class MyThread extends Thread { public void run() { /* task */ } }. In client code, create new MyThread().start();.
Step 2: Approach two: class MyTask implements Runnable { public void run() { /* task */ } }. Then in client code, new Thread(new MyTask()).start();.
Step 3: In both approaches, Thread.start() is what actually starts a new thread; directly calling run() would just execute code in the current thread.
Step 4: Evaluate the design implications. If you extend Thread, your class cannot extend any other base class and mixes task logic with threading logic.
Step 5: With Runnable, task logic lives in a separate class that can extend some other meaningful superclass, and you can run the same Runnable in different Thread instances or executor services.
Step 6: Option A correctly describes both approaches and explains why implementing Runnable is generally preferred in application code.
Verification / Alternative check:
Looking at modern Java concurrency utilities such as ExecutorService and ThreadPoolExecutor, you will notice they work primarily with Runnable and Callable tasks rather than requiring you to extend Thread. This demonstrates that separating task definition from thread management is a widely accepted best practice and supports the recommendation given in option A.
Why Other Options Are Wrong:
Option B is wrong because implementing Runnable and passing it to a Thread is a perfectly valid and common way to create real threads. Option C incorrectly states that both Thread and Runnable must always be implemented together, which is not necessary; you can extend Thread directly or you can implement Runnable and use a plain Thread instance. Option D is clearly incorrect and unrealistic, as Java provides built in facilities for creating threads without resorting to native code.
Common Pitfalls:
A frequent mistake is to override run() correctly but then call run() directly instead of calling start(), which does not create a new thread. Another pitfall is mixing business logic with thread management by extending Thread when a simple Runnable would be clearer. In modern applications, developers should often avoid creating raw Thread instances entirely and instead submit Runnables to an ExecutorService, but the underlying distinction between extending Thread and implementing Runnable remains important interview knowledge.
Final Answer:
You can create a thread either by extending Thread and overriding run() or by implementing Runnable and passing an instance to a Thread, and the preferred approach is usually to implement Runnable because it separates the task from the Thread and keeps inheritance flexible.
Discussion & Comments