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.