Calling Thread.run() Directly vs Thread.start()
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.
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();
}
}Running in: main
Running in: worker-bExplanation: 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
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));
}
}run(): about 900 ms? true
start(): under 600 ms? trueExplanation: 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 created | Yes | No |
| Runs in | The new thread | The calling thread |
Calls run() | Yes, in the new thread | It is the call |
| Can be called again | No: IllegalThreadStateException | Yes, any number of times |
| Thread state change | NEW to RUNNABLE | None |
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
- Easy: Print
Thread.currentThread().getName()inside a task called once withrun()and once withstart(). - Harder: Change the timing example to five tasks of 200 milliseconds and predict both times before running it.
Key Takeaways
Was this tutorial helpful?