10.2 Creating Threads
Multithreading enables multiple tasks to run concurrently within a single Java application. Java provides several ways to create threads, which are lightweight processes running within a program. This guide explores methods for creating threads in Java 8 and above, including implementing the Runnable interface and extending the Thread class.
1. Creating Threads by Implementing Runnable Interface
The Runnable interface is a functional interface that defines a single method, run(), which contains the code to execute in a thread. This method is passed to a Thread instance to start a new thread.
Example: Implementing Runnable Interface
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Thread is running using Runnable interface!");
}
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable); // Pass Runnable instance to Thread
thread.start(); // Start the thread
}
}
In this example, the MyRunnable class implements Runnable, and we pass its instance to a Thread object. Calling start() initiates the thread.
2. Creating Threads by Extending the Thread Class
Another way to create a thread is by extending the Thread class. This approach allows the thread to directly override the run() method.
Example: Extending Thread Class
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("Thread is running by extending Thread class!");
}
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start(); // Start the thread
}
}
Here, MyThread extends the Thread class and overrides run(). Starting this thread executes the code in run().
3. Using Lambda Expressions with Runnable (Java 8+)
Java 8 introduced lambda expressions, making it easier to create threads with the Runnable interface by reducing boilerplate code.
Example: Creating a Thread with Lambda Expression
public class LambdaThreadExample {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
System.out.println("Thread is running with Lambda expression!");
});
thread.start();
}
}
In this example, we use a lambda expression to define the Runnable directly in the Thread constructor, simplifying the code.
4. Using Executors for Thread Management (Java 8+)
The Executors framework provides a more flexible way to manage threads by handling thread creation, management, and pooling, which is especially useful for large applications.
Example: Using ExecutorService
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ExecutorExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(2); // Thread pool with 2 threads
// Submit tasks to executor
executor.submit(() -> System.out.println("Task 1 is running"));
executor.submit(() -> System.out.println("Task 2 is running"));
executor.shutdown(); // Shutdown the executor after task completion
}
}
In this example, ExecutorService creates a fixed thread pool of 2 threads, which manages task execution. Each task is a separate Runnable defined with a lambda expression.
Comparison of Approaches
| Method | Description | Pros | Cons |
|---|---|---|---|
| Implementing Runnable | Use with Runnable interface to define task in run() method. | Simple to implement, can separate task from thread. | Requires passing to Thread class. |
| Extending Thread | Extend Thread class and override run() method. | Directly associated with thread, easy to start. | Not recommended for reusability; limits inheritance. |
| Lambda with Runnable | Use lambda expression with Runnable for concise syntax. | Reduces boilerplate code. | Limited to Java 8 and above. |
| ExecutorService | Manages threads, offers pooling, and handles shutdown. | Great for managing multiple threads and complex tasks. | Adds slight overhead; requires shutdown management. |
Summary
- Runnable Interface: Implementing
Runnableis a common way to create threads, providing flexibility and allowing task and thread separation. - Thread Class: Extending
Threadis a straightforward approach but limits inheritance and reusability. - Lambda Expressions: Lambdas streamline
Runnablesyntax, improving readability. - ExecutorService: Best suited for complex applications, offering better thread management and efficiency.
Using these techniques allows you to create and manage threads in Java, improving application responsiveness and performance.