# Introduction to Threads

Course: [Java](https://www.devtutorials.ai/learn/java) · Section: Multithreading
Source: https://www.devtutorials.ai/learn/java/multithreading/introduction-to-threads

> Learn processes vs threads, the thread lifecycle, creating threads with Thread and Runnable, and using start, run, sleep, join and priorities.

Think of a restaurant kitchen.

If one cook had to do everything, the meal would take a long time. First they would chop the vegetables, then boil the rice, then fry the fish, then make the sauce, one after another.

In a real kitchen, several cooks work at the same time. One chops, one stirs the rice, and one fries the fish. They share the same kitchen, the same fridge, and the same stove. Dinner is ready much faster.

A Java program can work in the same way. Each "cook" is called a thread. Several threads can run inside one program and share its memory.

In simple words, a thread is one path of work inside a program, and multithreading means running several of those paths at the same time.

## What You Will Learn

In this lesson, you will learn:

1. What processes and threads are, and how they are different.
2. Why programs use multithreading.
3. The states in a thread's life.
4. How to create threads with the `Thread` class and the `Runnable` interface.
5. The difference between `start()` and `run()`.
6. How `sleep()` pauses a thread, and how `join()` waits for one to finish.
7. How thread priorities work.

## Start With a Working Example

Read the code, then look at the output below it.

```java readonly title="A second thread does work while main waits for it"
public class Main {
    public static void main(String[] args) throws InterruptedException {
        Thread worker = new Thread(() -> {
            int total = 0;
            for (int i = 1; i <= 100; i++) {
                total += i;
            }
            System.out.println("Worker: the sum of 1 to 100 is " + total);
        });

        System.out.println("Main: starting the worker");
        worker.start();
        worker.join();
        System.out.println("Main: the worker has finished");
    }
}
```

Output:

```text
Main: starting the worker
Worker: the sum of 1 to 100 is 5050
Main: the worker has finished
```

`main` creates a second thread called `worker` and gives it a job as a lambda. `start()` begins the thread. `join()` makes `main` wait until the worker is done.

Because `main` waits, the lines always appear in this order.

### Try This

Delete the line `worker.join();` and run the program on your own computer a few times. Sometimes "the worker has finished" prints **before** the worker's own line. Without `join`, `main` does not wait.

The in-browser Java runtime in this course runs on a single thread. Thread code compiles and runs here, but the threads take turns instead of truly running at the same time. Every example in this lesson waits for its threads with `join()`, so it prints the same result in the browser and on a real computer.

## What is a Process?

A **process** is a running program.

When you open a web browser, a music player, and a text editor, your computer runs three processes. Each process:

- has its **own memory**, which other processes cannot touch;
- has its own resources, such as open files;
- is managed by the operating system.

When you run a Java program, the operating system starts one process for the JVM.

## What is a Thread?

A **thread** is a single path of work **inside** a process. It runs one line of code after another.

Every Java program starts with one thread, called the **main thread**. It is the thread that runs your `main` method.

A process can create **more** threads. All the threads in a process:

- **share** the same memory, so they can read and change the same objects;
- each have their **own** call stack, so each runs its own methods with its own local variables.

```text
         One Java process
 ┌───────────────────────────────────┐
 │  Shared memory (heap): objects    │
 │                                   │
 │  ┌────────┐ ┌────────┐ ┌────────┐ │
 │  │ main   │ │ thread │ │ thread │ │
 │  │ thread │ │   2    │ │   3    │ │
 │  │ (stack)│ │ (stack)│ │ (stack)│ │
 │  └────────┘ └────────┘ └────────┘ │
 └───────────────────────────────────┘
```

You can ask Java which thread is running a line of code with `Thread.currentThread().getName()`.

## Process vs Thread

| | Process | Thread |
|---|---|---|
| What it is | A running program | A path of work inside a process |
| Memory | Its own, separate from others | Shared with the other threads in its process |
| Created by | The operating system | The program, inside its process |
| Cost to create | Higher | Lower |
| Talking to others | Hard, needs special channels | Easy, through shared objects |
| If one crashes | Other processes keep running | It can affect the whole process |
| Example | Your browser | One browser tab loading a page |

Think of a process as a house. Threads are the people living in it. Each house has its own rooms and doors. The people in one house share the kitchen and the living room.

## Why Multithreading Is Used

**Multithreading** means running several threads in one program at the same time.

### To Stay Responsive

A music app plays a song on one thread while you search for the next song on another. Without threads, the music would stop every time you typed.

### To Use Waiting Time

Programs often wait: for a file to load, a web page to answer, or a database to reply. While one thread waits, others can keep working.

### To Use Every CPU Core

Modern computers have several **cores**. Each core can run one thread at a time. A program with one thread uses only one core. A program with several threads can use them all and finish big jobs faster.

### To Keep Code Organised

Separate jobs, like "save a backup every minute" and "serve users", can each live in their own thread.

| Real application | What the threads do |
|---|---|
| Web server | One thread per user request, so many users are served at once |
| Video game | One thread draws the screen, another plays sound, another handles input |
| Download manager | Several threads download different parts of a file at once |
| Text editor | One thread for typing, one for spell-checking in the background |

## Thread Lifecycle

A thread moves through several **states** from creation to finish. Java names them in the `Thread.State` enum.

| State | Meaning |
|---|---|
| `NEW` | Created with `new`, but `start()` has not been called yet |
| `RUNNABLE` | Started; running, or ready to run as soon as a core is free |
| `BLOCKED` | Waiting to get a lock that another thread holds |
| `WAITING` | Waiting with no time limit, for example inside `join()` |
| `TIMED_WAITING` | Waiting for a set time, for example inside `sleep(500)` |
| `TERMINATED` | Finished; its `run` method has ended |

```text
          start()
   NEW ───────────► RUNNABLE ─────────────────► TERMINATED
                     ▲   │        run() ends
                     │   │
     lock is free /  │   │  waits for a lock, sleep(), join()
     time is up /    │   ▼
     other thread    BLOCKED / WAITING / TIMED_WAITING
     finished
```

A thread can move between `RUNNABLE` and the waiting states many times. But it starts `NEW` exactly once, and once it is `TERMINATED`, it can never run again.

You can read a thread's state with `getState()`. Read the code, then look at the output below it.

```java readonly title="A thread starts NEW and ends TERMINATED"
public class Main {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> System.out.println("  ...the thread is doing its job"));

        System.out.println("Before start: " + t.getState());
        t.start();
        t.join();
        System.out.println("After join: " + t.getState());
        System.out.println("Still alive? " + t.isAlive());
    }
}
```

Output:

```text
Before start: NEW
  ...the thread is doing its job
After join: TERMINATED
Still alive? false
```

The states in between change very quickly, so they are hard to catch in a print statement. `NEW` and `TERMINATED` are certain, because one is before `start()` and the other is after `join()`.

`isAlive()` returns `true` while a thread has started but not yet finished.

## Creating Threads

There are two common ways to give a thread its job.

### Thread Class

**Extend** the `Thread` class and **override** its `run()` method. The `run()` method holds the thread's job. Read the code, then look at the output below it.

```java readonly title="Create a thread by extending Thread"
public class Main {
    public static void main(String[] args) throws InterruptedException {
        Downloader d = new Downloader("report.pdf");
        d.start();
        d.join();
        System.out.println("main: download thread is done");
    }
}

class Downloader extends Thread {
    private final String file;

    Downloader(String file) {
        this.file = file;
    }

    @Override
    public void run() {
        for (int percent = 25; percent <= 100; percent += 25) {
            System.out.println("Downloading " + file + ": " + percent + "%");
        }
    }
}
```

Output:

```text
Downloading report.pdf: 25%
Downloading report.pdf: 50%
Downloading report.pdf: 75%
Downloading report.pdf: 100%
main: download thread is done
```

`Downloader` **is a** `Thread`, so you can call `start()` on it directly.

### Runnable Interface

**Implement** the `Runnable` interface and its one method, `run()`. Then pass the `Runnable` object to a `Thread`.

`Runnable` is a functional interface, so you can also write it as a lambda. Read the code, then look at the output below it.

```java readonly title="Create threads with Runnable, as a class and as a lambda"
public class Main {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new Greeter("Asha"), "greeter-1");

        Runnable task = () -> System.out.println("Hello from " + Thread.currentThread().getName());
        Thread t2 = new Thread(task, "greeter-2");

        t1.start();
        t1.join();
        t2.start();
        t2.join();

        System.out.println("Main thread is called: " + Thread.currentThread().getName());
    }
}

class Greeter implements Runnable {
    private final String name;

    Greeter(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println("Hello, " + name + "! I run on " + Thread.currentThread().getName());
    }
}
```

Output:

```text
Hello, Asha! I run on greeter-1
Hello from greeter-2
Main thread is called: main
```

The second argument to `new Thread(...)` gives the thread a name. That makes output and error messages much easier to read.

### Thread Class vs Runnable Interface

| | Extend `Thread` | Implement `Runnable` |
|---|---|---|
| How | `class X extends Thread` | `class X implements Runnable`, or a lambda |
| Can the class extend another class? | No, it already extends `Thread` | Yes |
| Job and thread are | Mixed together in one class | Kept separate |
| Can the same job run on several threads? | Not easily | Yes, pass it to several `Thread` objects |
| Recommended? | Only for simple cases | Yes, in most code |

**Easy Way to Remember:** `Runnable` is **the job**. `Thread` is **the worker** who does the job. Keeping them separate means one job can be handed to any worker.

## start()

`start()` creates a **new** thread and makes it run the `run()` method there.

- It returns straight away. The calling thread carries on while the new thread works.
- A thread can be started only **once**. Calling `start()` again throws an `IllegalThreadStateException`.

## run()

`run()` is the method that holds the thread's job. You write it, but you normally **do not call it yourself**.

If you call `run()` directly, **no new thread is created**. The code just runs as an ordinary method call, on the current thread. Read the code, then look at the output below it.

```java readonly title="run() runs on the current thread, start() makes a new one"
public class Main {
    public static void main(String[] args) throws InterruptedException {
        Runnable job = () -> System.out.println("Job is running on: " + Thread.currentThread().getName());

        Thread t = new Thread(job, "worker");

        System.out.println("Calling run():");
        t.run();

        System.out.println("Calling start():");
        t.start();
        t.join();
    }
}
```

Output:

```text
Calling run():
Job is running on: main
Calling start():
Job is running on: worker
```

`t.run()` ran the job on `main`, just like any other method. Only `t.start()` ran it on the new thread called `worker`.

| | `start()` | `run()` |
|---|---|---|
| Creates a new thread? | Yes | No |
| Where the job runs | On the new thread | On the thread that called it |
| Returns | Straight away | After the job finishes |
| Can be called twice? | No, throws `IllegalThreadStateException` | Yes, it is a normal method |
| Who calls it | You | The JVM, after `start()` |

## sleep()

`Thread.sleep(milliseconds)` pauses the **current** thread for at least that many milliseconds. 1000 milliseconds is 1 second.

While asleep, the thread is in the `TIMED_WAITING` state, and other threads can use the CPU.

`sleep` can throw an **`InterruptedException`**, a checked exception. It happens if another thread interrupts the sleeping thread. You must catch it or declare it with `throws`. Read the code, then look at the output below it.

```java readonly title="sleep pauses the current thread"
public class Main {
    public static void main(String[] args) throws InterruptedException {
        String[] lights = {"Red", "Amber", "Green"};

        for (String light : lights) {
            System.out.println("Traffic light: " + light);
            Thread.sleep(200);
        }

        System.out.println("Cycle complete");
    }
}
```

Output:

```text
Traffic light: Red
Traffic light: Amber
Traffic light: Green
Cycle complete
```

Each line appears about 0.2 seconds after the one before it. `sleep` is useful for timers, animations, and waiting a little before trying something again.

Never use `sleep` to "make sure" another thread has finished. The timing can change from run to run. Use `join()` for that.

## join()

`join()` makes the **current** thread wait until another thread has **finished**.

- `t.join()` waits with no time limit.
- `t.join(500)` waits at most 500 milliseconds.

Use `join` when you need a thread's result before carrying on. Read the code, then look at the output below it.

```java readonly title="join waits for two workers before adding up their results"
public class Main {
    static long firstHalf;
    static long secondHalf;

    public static void main(String[] args) throws InterruptedException {
        Thread a = new Thread(() -> firstHalf = sum(1, 500_000));
        Thread b = new Thread(() -> secondHalf = sum(500_001, 1_000_000));

        a.start();
        b.start();

        a.join();
        b.join();

        System.out.println("First half: " + firstHalf);
        System.out.println("Second half: " + secondHalf);
        System.out.println("Total: " + (firstHalf + secondHalf));
    }

    static long sum(int from, int to) {
        long total = 0;
        for (int i = from; i <= to; i++) {
            total += i;
        }
        return total;
    }
}
```

Output:

```text
First half: 125000250000
Second half: 375000250000
Total: 500000500000
```

The two threads add up different halves of the numbers. On a computer with two or more cores, they can work at the same time.

`main` calls `join()` on both before reading their results. Without the joins, `main` might print `0` because the threads had not finished yet.

Never read a result that another thread is still writing. Wait for it first, with `join()` or another proper tool. Reading too early gives old or half-finished values, and the bug may only appear on some runs.

## Thread Priorities

Every thread has a **priority**: a number from **1** (lowest) to **10** (highest). A new thread gets the priority of the thread that created it, usually **5**.

`Thread` has three constants:

| Constant | Value |
|---|---|
| `Thread.MIN_PRIORITY` | 1 |
| `Thread.NORM_PRIORITY` | 5 |
| `Thread.MAX_PRIORITY` | 10 |

You read a priority with `getPriority()` and change it with `setPriority(n)`. Read the code, then look at the output below it.

```java readonly title="Read and set thread priorities"
public class Main {
    public static void main(String[] args) {
        Thread background = new Thread(() -> {}, "backup");
        Thread urgent = new Thread(() -> {}, "alarm");
        Thread normal = new Thread(() -> {}, "worker");

        background.setPriority(Thread.MIN_PRIORITY);
        urgent.setPriority(Thread.MAX_PRIORITY);

        System.out.println("main: " + Thread.currentThread().getPriority());
        System.out.println(background.getName() + ": " + background.getPriority());
        System.out.println(urgent.getName() + ": " + urgent.getPriority());
        System.out.println(normal.getName() + ": " + normal.getPriority());
    }
}
```

Output:

```text
main: 5
backup: 1
alarm: 10
worker: 5
```

`worker` was never changed, so it kept the default, `5`.

### Priorities Are Only Hints

A higher priority **suggests** to the operating system that a thread should get more CPU time. It does **not** guarantee it. Different operating systems treat priorities differently, and some mostly ignore them.

So never write a program that is only correct if threads run in priority order. Use `join()` and the tools in the next lesson to control order.

Setting a priority outside 1 to 10 throws an `IllegalArgumentException`.

## Common mistakes

The following mistakes are common when creating and starting threads. Each row shows what you see, why it happens and how to fix it.

| Symptom | Cause | Fix |
|---|---|---|
| The job runs on `main` | `run()` was called instead of `start()` | Call `start()` |
| `IllegalThreadStateException` | `start()` was called twice | Create a new `Thread` object |
| `unreported exception InterruptedException` | `sleep` or `join` is not handled | Add `throws InterruptedException`, or catch it |
| A result prints as `0` or old data | It was read before the thread finished | Call `join()` first |
| Output lines change order between runs | Threads ran without waiting for each other | Use `join()` where order matters |
| `IllegalArgumentException` from `setPriority` | The priority was outside 1 to 10 | Use a value from 1 to 10 |

## What to Remember

- A process is a running program with its own memory. A thread is a path of work inside it, sharing that memory.
- Multithreading keeps programs responsive, uses waiting time, and spreads work across CPU cores.
- A thread moves from `NEW` to `RUNNABLE`, can wait (`BLOCKED`, `WAITING`, `TIMED_WAITING`), and ends `TERMINATED`.
- Create threads by extending `Thread`, or, better, by passing a `Runnable` (often a lambda) to a `Thread`.
- `start()` creates a new thread. Calling `run()` directly does not.
- `sleep()` pauses the current thread. `join()` waits for another thread to finish.
- Priorities run from 1 to 10. They are hints, not guarantees.

Next: Synchronization
