Calling Thread.run() Directly vs Thread.start()

Naveen AnamaneniUpdated · 3 min read

start() and run() look similar, but they do very different things. start() asks the JVM to create a new thread, and that new thread then calls run(). Calling run() directly is just an ordinary method call: the code runs in the current thread, and no new thread is created. In this lesson, you will see the difference by printing thread names and measuring time.

Example: Thread Names

Let's see an example that calls run() and start() on two threads.

RunVsStart.java▶ Try it
public class RunVsStart {
    public static void main(String[] args) throws InterruptedException {
        Runnable task = () -> System.out.println("Running in: "
            + Thread.currentThread().getName());
 
        Thread a = new Thread(task, "worker-a");
        a.run();
 
        Thread b = new Thread(task, "worker-b");
        b.start();
        b.join();
    }
}
Output
Running in: main
Running in: worker-b

Explanation: a.run() ran the task in the main thread, as a plain method call; worker-a was never started. b.start() created the thread worker-b, which ran the task.

Example: Timing

Timing.java▶ Try it
public class Timing {
    static void slowTask() {
        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
 
    static long measure(boolean useStart) throws InterruptedException {
        long begin = System.nanoTime();
        Thread[] threads = new Thread[3];
        for (int i = 0; i < 3; i++) {
            threads[i] = new Thread(Timing::slowTask);
            if (useStart) {
                threads[i].start();
            } else {
                threads[i].run();
            }
        }
        for (Thread t : threads) {
            if (useStart) {
                t.join();
            }
        }
        return (System.nanoTime() - begin) / 1_000_000;
    }
 
    public static void main(String[] args) throws InterruptedException {
        System.out.println("run(): about 900 ms? " + (measure(false) >= 900));
        System.out.println("start(): under 600 ms? " + (measure(true) < 600));
    }
}
Output
run(): about 900 ms? true
start(): under 600 ms? true

Explanation: With run(), the three 300-millisecond tasks happen one after another in the main thread, taking about 900 milliseconds. With start(), they run in three threads at the same time, so the whole job takes only a little over 300 milliseconds.

Differences Between start() and run()

start()run() called directly
New thread createdYesNo
Runs inThe new threadThe calling thread
Calls run()Yes, in the new threadIt is the call
Can be called againNo: IllegalThreadStateExceptionYes, any number of times
Thread state changeNEW to RUNNABLENone

Common Mistakes

Calling run() and expecting parallel work. Everything runs one after another in the current thread.

Overriding start() instead of run(). The thread may never be created.

Interview Corner

What happens if you call run() instead of start()?

The run() method executes like any normal method, in the calling thread's stack; no new thread is created, so there is no concurrency, and Thread.currentThread() inside it returns the caller. Only start() asks the JVM to create a new thread, which then invokes run(). Also, start() may be called only once, while run() can be called repeatedly.

Practice

  1. Easy: Print Thread.currentThread().getName() inside a task called once with run() and once with start().
  2. Harder: Change the timing example to five tasks of 200 milliseconds and predict both times before running it.

Quick check: run() vs start()

4 questions
1Which method creates a new thread?
2In which thread does t.run() execute?
3Can run() be called more than once?
4Which method should you override to give a thread work?

Key Takeaways

Was this tutorial helpful?

Lesson 385 of 482