Java Threads


In Java, a thread is a lightweight process that allows multiple parts of a program to run simultaneously. Multithreading helps improve performance by executing two or more tasks at the same time. For example, one thread might handle user input while another performs background processing.

Java provides built-in support for threads through the java.lang.Thread class and the Runnable interface. Understanding threads is essential for building responsive, efficient, and real-time Java applications.

What Is a Thread?

A thread is the smallest unit of a process that can execute independently. Every Java program runs at least one thread — the main thread — which starts automatically when you run the program. You can create additional threads to perform separate tasks concurrently.

When multiple threads execute together, the program is said to be multithreaded.

Benefits of Multithreading

  • Better performance: Tasks can be performed simultaneously.

  • Faster execution: Time-consuming operations can run in the background.

  • Efficient CPU use: Threads utilize idle CPU cycles.

  • Responsive UI: Useful in GUI-based programs where the interface should not freeze.

Creating Threads in Java

There are two common ways to create threads:

1. Extending the Thread Class

You can create a thread by extending the Thread class and overriding its run() method.

class MyThread extends Thread {
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println("Running thread: " + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                System.out.println(e);
            }
        }
    }
}

public class ThreadExample {
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        t1.start();
    }
}

Here’s how it works:

  • run() contains the code that executes inside the thread.

  • start() begins execution in a separate thread.

  • sleep(500) pauses the thread for 500 milliseconds.

2. Implementing the Runnable Interface

Another approach is to implement the Runnable interface and pass it to a Thread object.

class MyRunnable implements Runnable {
    public void run() {
        for (int i = 1; i <= 3; i++) {
            System.out.println("Runnable thread: " + i);
        }
    }
}

public class RunnableExample {
    public static void main(String[] args) {
        Thread t = new Thread(new MyRunnable());
        t.start();
    }
}

The Runnable interface is preferred in real-world applications because it separates the thread logic from the thread object and allows a class to extend another superclass.

Thread States

A thread in Java passes through different stages in its life cycle. These are:

State Description
New The thread is created but not yet started.
Runnable The thread is ready to run but waiting for CPU time.
Running The thread is currently executing.
Blocked/Waiting The thread is waiting for a resource or another thread.
Terminated The thread has finished execution.

Example:

Thread t = new Thread();
System.out.println(t.getState()); // NEW
t.start();
System.out.println(t.getState()); // RUNNABLE

Thread Methods

Java’s Thread class provides several useful methods:

Method Description
start() Starts the thread and calls run().
run() Defines the code executed by the thread.
sleep(ms) Pauses the thread for the given milliseconds.
join() Waits for a thread to finish execution.
isAlive() Checks if a thread is still running.
setName() Sets the thread’s name.
getName() Returns the thread’s name.
setPriority() Sets the thread’s priority (1–10).

Example:

class DemoThread extends Thread {
    public void run() {
        System.out.println(Thread.currentThread().getName() + " is running");
    }
}

public class ThreadMethods {
    public static void main(String[] args) {
        DemoThread t1 = new DemoThread();
        DemoThread t2 = new DemoThread();

        t1.setName("Worker-1");
        t2.setName("Worker-2");

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

        System.out.println("Is Worker-1 alive? " + t1.isAlive());
    }
}

Thread Priority

Every thread has a priority that helps the scheduler decide which thread to execute first. Priority values range from 1 (MIN_PRIORITY) to 10 (MAX_PRIORITY), with the default being 5 (NORM_PRIORITY).

Example:

t1.setPriority(Thread.MAX_PRIORITY);
t2.setPriority(Thread.MIN_PRIORITY);

However, priority behavior can vary depending on the operating system’s thread scheduler.

Thread Synchronization

When multiple threads share the same data or resources, synchronization ensures that only one thread accesses that resource at a time. Without it, data inconsistency or race conditions can occur.

Example: Without Synchronization

class Counter {
    int count = 0;
    void increment() {
        count++;
    }
}

public class SyncProblem {
    public static void main(String[] args) throws InterruptedException {
        Counter c = new Counter();

        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) c.increment();
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) c.increment();
        });

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

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

        System.out.println("Count: " + c.count);
    }
}

You might get different results because both threads modify count simultaneously.

Example: With Synchronization

class Counter {
    int count = 0;
    synchronized void increment() {
        count++;
    }
}

Using the synchronized keyword ensures that only one thread executes the increment() method at a time.

Daemon Threads

A daemon thread runs in the background to perform tasks like garbage collection. These threads automatically stop when all user threads finish.

class BackgroundTask extends Thread {
    public void run() {
        while (true) {
            System.out.println("Daemon thread running...");
            try { Thread.sleep(1000); } catch (Exception e) {}
        }
    }
}

public class DaemonExample {
    public static void main(String[] args) {
        BackgroundTask t = new BackgroundTask();
        t.setDaemon(true);
        t.start();
        System.out.println("Main thread ends");
    }
}

Output:

Daemon thread running...
Main thread ends

After the main thread ends, the daemon thread also stops automatically.

Summary of the Tutorial

Threads in Java allow multiple tasks to run concurrently, improving performance and responsiveness. You can create threads by extending the Thread class or implementing Runnable.

Key features like synchronization, thread priority, and daemon threads help control execution and prevent conflicts. Understanding threading is essential for writing efficient and reliable Java applications.


Practice Questions

  1. Write a Java program to create a thread by extending the Thread class that prints numbers from 1 to 10 with a 1-second delay.

  2. Create a thread by implementing the Runnable interface that prints your name five times.

  3. Write a Java program that starts two threads — one prints even numbers and the other prints odd numbers up to 20.

  4. Create a program that demonstrates the use of sleep() and isAlive() methods on multiple threads.

  5. Write a Java program that sets custom thread names and priorities for three threads and prints them while they run.

  6. Create a synchronized method increment() in a class and run two threads that call it 1000 times each. Print the final count to verify synchronization.

  7. Write a Java program that uses join() to make sure one thread finishes before another starts.

  8. Create a daemon thread that prints “Background task running…” every second, while the main thread prints “Main task complete.”

  9. Write a Java program that shows the different thread states (NEW, RUNNABLE, TERMINATED) using getState().

  10. Build a Java program that simulates a simple banking scenario where multiple threads deposit and withdraw money safely using synchronization.


Try a Short Quiz.

coding learning websites codepractice

No quizzes available.

Go Back Top