Home ยป Java Threads Tutorial: Creating a Thread

Java Threads Tutorial: Creating a Thread

Java provides built-in support for multithreading, allowing you to execute multiple threads simultaneously to achieve concurrent execution.

Threads in Java are lightweight processes that allow your program to perform multiple tasks at the same time, which can significantly improve the performance of your application.

This tutorial will guide you through different ways of creating and managing threads in Java, with examples for each method.

What is a Thread?

A thread is a small unit of a process that executes independently. Java provides two ways to create a thread:

By extending the Thread class.
By implementing the Runnable interface.

1. Creating a Thread by Extending the Thread Class

The Thread class itself provides methods to create and manage threads. By extending the Thread class, we can create our own thread.

Steps:

Extend the Thread class.
Override the run() method, which contains the code that the thread will execute.
Create an instance of the class and call the start() method to begin execution of the thread.

Example:

// Creating a thread by extending the Thread class
class MyThread extends Thread {
    @Override
    public void run() {
        // Code that will be executed by the thread
        System.out.println("Thread is running: " + Thread.currentThread().getName());
    }
}

public class ThreadExample {
    public static void main(String[] args) {
        // Creating a new thread
        MyThread thread = new MyThread();
        thread.start();  // Start the thread
    }
}

Explanation:

We extend the Thread class and override the run() method to define the task for the thread.
The start() method is called to begin the thread execution, which internally calls the run() method.
Output will show the thread name:

Thread is running: Thread-0

2. Creating a Thread by Implementing the Runnable Interface

Another way to create a thread in Java is by implementing the Runnable interface.

This approach is more flexible since it allows the class to extend another class, while still enabling multithreading.

Steps:
Implement the Runnable interface in your class.
Override the run() method.
Create an instance of Thread by passing a Runnable object, and call start().

Example:

// Creating a thread by implementing the Runnable interface
class MyRunnable implements Runnable {
    @Override
    public void run() {
        // Code that will be executed by the thread
        System.out.println("Thread is running: " + Thread.currentThread().getName());
    }
}

public class RunnableExample {
    public static void main(String[] args) {
        // Creating a Runnable object
        MyRunnable runnable = new MyRunnable();
        
        // Creating a thread with the Runnable object
        Thread thread = new Thread(runnable);
        thread.start();  // Start the thread
    }
}

Explanation:

We implement the Runnable interface and override the run() method.
The Thread object is created by passing the Runnable object, and start() is called to execute the thread.
Output will show the thread name:

Thread is running: Thread-0

3. Using an Anonymous Class to Create a Thread

You can also create a thread using an anonymous class that implements the Runnable interface, which avoids explicitly defining a new class.

Example:

public class AnonymousClassThread {
    public static void main(String[] args) {
        // Creating a thread using an anonymous class
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Thread is running: " + Thread.currentThread().getName());
            }
        });
        
        thread.start();  // Start the thread
    }
}

Explanation:

An anonymous class implementing Runnable is passed to the Thread constructor.
This is a compact way to create threads if you donโ€™t want to create a separate class.

Output:

Thread is running: Thread-0

4. Creating a Thread using Lambda Expressions (Java 8+)

From Java 8 onwards, you can use lambda expressions to simplify thread creation, especially when using functional interfaces like Runnable.

Example:

public class LambdaThread {
    public static void main(String[] args) {
        // Creating a thread using a lambda expression
        Thread thread = new Thread(() -> {
            System.out.println("Thread is running using Lambda: " + Thread.currentThread().getName());
        });

        thread.start();  // Start the thread
    }
}

Explanation:

Lambda expressions provide a concise way to create threads by eliminating the need for boilerplate code.

Output:

Thread is running using Lambda: Thread-0

5. Passing Parameters to Threads

You can pass parameters to threads by using the constructor of the class that implements Runnable or extends Thread.

Example:

// A class that implements Runnable and takes a parameter
class MyRunnableWithParams implements Runnable {
    private String name;

    public MyRunnableWithParams(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println("Thread " + name + " is running");
    }
}

public class ThreadWithParams {
    public static void main(String[] args) {
        // Passing parameters to threads
        MyRunnableWithParams runnable1 = new MyRunnableWithParams("Thread 1");
        MyRunnableWithParams runnable2 = new MyRunnableWithParams("Thread 2");

        Thread thread1 = new Thread(runnable1);
        Thread thread2 = new Thread(runnable2);

        thread1.start();
        thread2.start();
    }
}

Explanation:

Parameters are passed to the Runnable object through the constructor.

Output:

Thread Thread 1 is running
Thread Thread 2 is running

6. Getting and Setting Thread Names

Each thread has a name, and you can set or get the name of a thread using getName() and setName() methods.

Example:

public class ThreadNameExample {
    public static void main(String[] args) {
        // Creating a thread and setting its name
        Thread thread = new Thread(() -> {
            System.out.println("Thread is running: " + Thread.currentThread().getName());
        });

        thread.setName("MyCustomThread");
        thread.start();  // Start the thread

        // Getting the main thread name
        System.out.println("Main thread name: " + Thread.currentThread().getName());
    }
}

Explanation:

We use setName() to set a custom name for the thread.

Output:

Thread is running: MyCustomThread
Main thread name: main

7. Using join() to Wait for Threads to Finish

The join() method allows one thread to wait for another thread to finish its execution before continuing.

Example:

public class JoinThreadExample {
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            System.out.println("Thread 1 is running");
            try {
                Thread.sleep(2000);  // Simulate some work with sleep
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Thread 1 has finished");
        });

        Thread thread2 = new Thread(() -> {
            System.out.println("Thread 2 is running");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Thread 2 has finished");
        });

        thread1.start();
        thread2.start();

        try {
            // Wait for both threads to finish
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("All threads have finished");
    }
}

Explanation:

The join() method ensures that the main thread waits for thread1 and thread2 to finish before printing the final message.

Output:

Thread 1 is running
Thread 2 is running
Thread 2 has finished
Thread 1 has finished
All threads have finished

8. Using sleep() to Pause Execution

The sleep() method pauses the execution of a thread for a specified number of milliseconds.

Example:

public class SleepThreadExample {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            System.out.println("Thread started");
            try {
                Thread.sleep(3000);  // Pauses the thread for 3 seconds
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Thread resumed after sleep");
        });

        thread.start();
    }
}

Explanation:

The sleep() method pauses the thread for 3 seconds before resuming.

Output:

Thread started
Thread resumed after sleep

9. Handling InterruptedException

The InterruptedException is thrown when a thread is interrupted while it is sleeping, waiting, or otherwise paused.

Example:

public class InterruptedExceptionExample {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            try {
                System.out.println("Thread is sleeping...");
                Thread.sleep(5000);  // Pauses for 5 seconds
            } catch (InterruptedException e) {
                System.out.println("Thread was interrupted!");
            }
        });

        thread.start();

        // Interrupt the thread after 2 seconds
        try {
            Thread.sleep(2000);
            thread.interrupt();  // Interrupt the thread
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Explanation:

The interrupt() method interrupts a thread, which causes it to throw an InterruptedException if it is sleeping or waiting.

Output:

Thread is sleeping...
Thread was interrupted!

Conclusion

Threads in Java allow concurrent execution of tasks, improving the performance and responsiveness of applications.

There are two main ways to create a thread: by extending the Thread class or by implementing the Runnable interface. Both approaches offer flexibility depending on the design needs of your application.

By understanding how to create, manage, and control thread behavior (e.g., using join(), sleep(), and interrupt()), you can effectively use multithreading to build efficient Java applications.

You may also like