Introduction to threads
Running more than one thing at once, the main thread, and creating a thread two ways.
Finished reading?
Mark this session so you can track where you are.
Running more than one thing at once, the main thread, and creating a thread two ways.
Finished reading?
Mark this session so you can track where you are.
Every program you have written so far has done one thing at a time. It starts at the top ofmain, runs down line by line, and stops at the bottom. That is a fine way to live, until the day you want two things to happen at once: keep a screen responsive while a long download runs, or time a slow job while another slow job is already going. This session is about that second worker.
Thread and by implementing Runnable.start() apart from run(), and say why calling run() yourself is a trap.When you run a Java program, the JVM does not just start executing your code out of nowhere. It creates a thread for you and points it at main. A thread is an independent path of execution: a single worker walking through your code one statement at a time, keeping its own place in the line. The worker the JVM made for you is called the main thread, and so far it has done all of your work, alone.
Here is proof that the worker has a name. Java will tell you which thread is currently running.
public class Main { public static void main(String[] args) { String me = Thread.currentThread().getName(); System.out.println("Running on thread: " + me); }}Running on thread: main
Thread.currentThread() hands back the thread doing the work right now, and getName() reads its name. Since only the main thread exists, the answer is main. One worker, one path, top to bottom. The whole idea of this session is: what if you could hire a second worker?
Java already has a class called Thread that knows how to be a worker. It has one method, run(), that holds the job the worker should do. Out of the box that method is empty. So the first way to make your own thread is the inheritance you already know: write a class that extends Thread and override run() with the work you want done.
public class Main { public static void main(String[] args) { Greeter g = new Greeter(); g.start(); System.out.println("main says hello"); }} class Greeter extends Thread { public void run() { System.out.println("worker says hello"); }}(one possible run; order varies) main says hello worker says hello
Read it slowly. Greeter is an ordinary class that happens to extend Thread, and its run() method holds one print. In main we build a Greeter object the usual way, then call g.start(). That single call asks the JVM to spin up a brand new thread, a second worker, and that new worker runs the run() method. Meanwhile the main thread does not wait around. It carries straight on to its own println. Now two workers are loose at the same time, each printing one line.
start()launches a new thread and that new thread runsrun(). The thread that calledstart()keeps going without waiting.
main says hello first; on the very next run, with the same code, you might see worker says hello first. We are showing one plausible ordering, not the only one. Do not write code that depends on which line wins.Extending Thread works, but it spends your one chance at inheritance. A Java class can only extend one parent. If Greeter already needed to extend some other class, it could not also extend Thread. The second way sidesteps that. Instead of being a thread, you write a class that is only the job, and you hand that job to a thread.
The job is described by an interface called Runnable. It has exactly one method, run(). You write a class that implements Runnable, put your work in run(), then create a real Thread and pass your job into it. The thread holds your job and runs it.
public class Main { public static void main(String[] args) { Task job = new Task(); Thread t = new Thread(job); t.start(); System.out.println("main is done"); }} class Task implements Runnable { public void run() { System.out.println("task is running"); }}(one possible run; order varies) main is done task is running
The shape is slightly different but the idea is the same. Task is not a thread; it is just a bundle of work, an object whose run() says what to do. We then make a real Thread and pass job into its constructor, so the thread knows what job to run. Calling t.start() launches the new worker, and the new worker runs job's run(). Same outcome as before, same un-nailed-down ordering, but Task kept its inheritance free.
Thread runs it.myObject.start().new Thread(job).start().run() from the Thread class.run() from the Runnable interface.extends Thread first because it has fewer moving parts. But in real projects the Runnable version is the common choice, precisely because it does not burn your single inheritance slot. If you only remember one, remember this one.This is the single most common mistake people make with threads, and it is worth slowing down for. Your worker's job lives in a method called run(). It is tempting to start the worker by calling run(). That does something, but not what you want.
public class Main { public static void main(String[] args) { Greeter g = new Greeter(); g.run(); System.out.println("main on thread: " + Thread.currentThread().getName()); }} class Greeter extends Thread { public void run() { System.out.println("worker on thread: " + Thread.currentThread().getName()); }}worker on thread: main main on thread: main
Look closely at this output and compare it with the very first Greeter example. There are two tells that something went wrong. First, the order is no longer in question: it is exactly worker then main, every single time. Second, and this is the giveaway, both lines say they ran on the main thread. No second worker was ever created.
Calling
run()directly is just an ordinary method call. It runs the job on the current thread, in order, and no new thread is born. Onlystart()creates a new thread.
g.run()is a perfectly legal method call, and the program runs and prints. It just quietly does the wrong thing: your “background” work happens right there on the main thread while everything else waits, and you have gained nothing. If your threaded program behaves as if it is single-threaded, this is the first thing to check.Let us make the unpredictability impossible to miss. Here we start three workers, each printing the same line, and let the main thread print too.
public class Main { public static void main(String[] args) { for (int i = 1; i <= 3; i = i + 1) { Worker w = new Worker(i); w.start(); } System.out.println("main: hi"); }} class Worker extends Thread { int id; Worker(int id) { this.id = id; } public void run() { System.out.println("worker " + id + ": hi"); }}(one possible run; a different run can reorder every line) main: hi worker 2: hi worker 1: hi worker 3: hi
We started the workers in order, 1 then 2 then 3, yet the output above shows 2 before 1, and mainjumping ahead of all of them. That is not a bug in the example. It is the honest truth about threads: once they are running, the order their lines appear in is decided moment to moment by the operating system's scheduler, and it can differ on every run. The numbers will all appear; which comes first is anyone's guess.
Try each one yourself first, then open the answer.
class Job extends Thread with a run() method, built Job j = new Job(), and then called j.run() instead of j.start(). Did a new thread get created? What runs the job?Runnable example, which object goes inside new Thread(...), the Task or the Thread? What does the thread do with it?Take these away. They continue exactly what we just did.
Race example and run it five times in a row. Write down the order of the four lines each time. Note whether any two runs matched, and write one sentence on what that tells you about thread ordering.Greeter example (the extends Thread one) so it uses Runnable instead. Keep the printed line the same. You should end up creating a separate Thread object and passing your job into it.start() to run(). Before running it, predict in writing what you expect to see and why. Then run it and add a line inside run() that prints Thread.currentThread().getName() to confirm which thread actually did the work.