Create and run threads by extending the Thread class or implementing the Runnable interface.
In Java, there are two primary ways to define a task that can be executed by a thread. The first method is to create a class that extends the `java.lang.Thread` class. This class must override the `run()` method, which contains the code that will be executed in the new thread. To start the execution, you create an instance of your custom thread class and call its `start()` method. The `start()` method is crucial; it allocates system resources for the new thread, schedules it to be run by the JVM, and calls the `run()` method in the new thread of execution. Calling `run()` directly would simply execute the code in the current thread, not a new one. The second, and generally preferred, method is to create a class that implements the `java.lang.Runnable` interface. The `Runnable` interface has a single method, `run()`. You place your task's logic inside this method. To execute this `Runnable`, you create an instance of it, then create a new `Thread` object, passing your `Runnable` instance to the `Thread`'s constructor. Finally, you call the `start()` method on the `Thread` object. The reason implementing `Runnable` is often better is that Java does not support multiple inheritance of classes. If your class already needs to extend another class, it cannot also extend `Thread`. By implementing `Runnable`, your class is free to extend any other class while still being executable by a thread. This approach promotes better object-oriented design by separating the task (the `Runnable`) from the execution mechanism (the `Thread`).