Getting Started with Threads in Java: Runnable vs Thread

When you start learning about multithreading in Java, one of the first things you’ll come across is how to create a thread. Threads allow your program to run multiple tasks at the same time, which is especially useful for tasks like handling multiple requests, downloading files, or running background operations without freezing your application.

In Java, there are two main ways to create threads:

  1. By implementing the Runnable interface

  2. By extending the Thread class

Let’s explore both with a simple example.


The Runnable Way

The most common and flexible approach is to use the Runnable interface. Here’s what it looks like:

static class MyThreadWithRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Class MyThread implementing Runnable: " + Thread.currentThread().getName());
    }
}

Here’s what’s happening:

  • We implement the Runnable interface, which requires us to define the run() method.

  • The run() method contains the code that will be executed when the thread starts.

  • To start the thread, we pass this class into a Thread object and call .start():

Thread t1 = new Thread(new MyThreadWithRunnable());
t1.start();

Extending the Thread Class

Another option is to extend the Thread class directly:

static class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Class MyThread extending Thread: " + Thread.currentThread().getName());
    }
}

This works in a similar way:

  • We extend Thread instead of implementing Runnable.

  • We override the run() method with our custom code.

  • To start the thread, we just create an instance and call .start():

MyThread t2 = new MyThread();
t2.start();

Putting It All Together

In the main() method, we can try both approaches:

public static void main(String[] args) {
    System.out.println("Current Thread: " + Thread.currentThread().getName());

    Thread t1 = new Thread(new MyThreadWithRunnable()); // Runnable example
    MyThread t2 = new MyThread();                      // Extending Thread example

    t1.start();
    t2.start();
}

When you run this program, you’ll see output like:

Current Thread: main
Class MyThread implementing Runnable: Thread-0
Class MyThread extending Thread: Thread-1

The exact order may vary, because thread scheduling is managed by the JVM and the operating system.


Runnable vs Thread: Which Should You Use?

Both approaches work, but in practice:

  • Prefer Runnable:

    • Java doesn’t support multiple inheritance, so if your class already extends another class, you can’t extend Thread.

    • Using Runnable keeps your code more flexible and decoupled.

  • Use Thread only when you don’t need to extend another class and want a very quick implementation.

  • Threads allow Java programs to run tasks concurrently.

  • Always call .start() (not .run()) to execute code in a new thread.

  • You can create threads either by implementing Runnable or extending Thread.

  • Execution order may vary because it depends on the JVM and OS thread scheduler.


Conclusion

Understanding how to create and start threads is the first step in working with concurrency in Java. While this example is simple, real-world multithreaded programs often involve synchronization, thread pools, and concurrency utilities from the java.util.concurrent package.

But as a beginner, mastering these two basic thread creation techniques is a great starting point.

You can check this code on github

0
Subscribe to my newsletter

Read articles from Luis Gustavo Souza directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Luis Gustavo Souza
Luis Gustavo Souza