Extending the Thread class: NOT preferred but in some cases necessary when you need to modify the behavior of the thread itself (e.g priority)
Implementing the Runnable interface: This is very common. Allows compatibility with the Concurrency API, seperation of concerts (describe the task the thread should do, not the thread itself). Also since Runnable is a @Functional Interface, it's easy to use them with lambdas:
new Thread( () -> System.out.Println('Hello From Thread'); ).start();
Using the ExecutorService: This is the preferred method. though it doesnt create threads directly - you simply submit tasks. Preferred since it automatically manages thread pools, etc
A couple of options: