In Java, there are several ways to create and manage threads. Here’s an overview of the primary methods:
1. Extending the Thread Class
- You can create a class that extends the
Threadclass and overrides itsrunmethod.
class MyThread extends Thread {
@Override
public void run() {
System.out.println("Thread is running...");
}
}
public class Main {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start(); // Start the thread
}
}
2. Implementing the Runnable Interface
- Create a class that implements the
Runnableinterface and pass it to aThreadobject.
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Thread is running...");
}
}
public class Main {
public static void main(String[] args) {
Thread thread = new Thread(new MyRunnable());
thread.start();
}
}
3. Using an Anonymous Class
- You can create a thread by directly instantiating
Runnableas an anonymous class.
public class Main {
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Thread is running...");
}
});
thread.start();
}
}
4. Using Lambda Expressions (Java 8+)
- Use a lambda expression to simplify thread creation.
public class Main {
public static void main(String[] args) {
Thread thread = new Thread(() -> System.out.println("Thread is running..."));
thread.start();
}
}
5. Using the Executor Framework (Recommended for Thread Pool Management)
- Use the
Executorframework to manage threads efficiently, especially for multiple tasks.
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.execute(() -> System.out.println("Task 1 is running..."));
executor.execute(() -> System.out.println("Task 2 is running..."));
executor.shutdown(); // Shutdown the executor
}
}
6. Using the Callable Interface and Future
Callableis similar toRunnablebut allows returning a result or throwing an exception.
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Main {
public static void main(String[] args) throws Exception {
ExecutorService executor = Executors.newSingleThreadExecutor();
Callable<String> task = () -> {
return "Task is completed!";
};
Future<String> future = executor.submit(task);
System.out.println(future.get()); // Get the result
executor.shutdown();
}
}
7. Using ForkJoinPool for Recursive Tasks (Java 7+)
- Use for parallelism with tasks that can be divided into subtasks.
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.ForkJoinPool;
class SumTask extends RecursiveTask<Integer> {
private int start, end;
public SumTask(int start, int end) {
this.start = start;
this.end = end;
}
@Override
protected Integer compute() {
if (end - start <= 10) {
int sum = 0;
for (int i = start; i <= end; i++) {
sum += i;
}
return sum;
} else {
int mid = (start + end) / 2;
SumTask leftTask = new SumTask(start, mid);
SumTask rightTask = new SumTask(mid + 1, end);
leftTask.fork();
rightTask.fork();
return leftTask.join() + rightTask.join();
}
}
}
public class Main {
public static void main(String[] args) {
ForkJoinPool pool = new ForkJoinPool();
SumTask task = new SumTask(1, 100);
int result = pool.invoke(task);
System.out.println("Sum: " + result);
}
}
Comparison of Thread Creation Methods in Java
| Method | When to Use | Pros | Cons |
|---|---|---|---|
| Extending Thread Class | When simple thread creation is needed, and no other class needs to be extended. | Easy to implement, direct thread control. | Cannot extend any other class due to Java’s single inheritance limitation. |
| Implementing Runnable Interface | When a task needs to be executed in a separate thread without modifying thread behavior. | More flexible, allows extending other classes. | Requires explicit creation of Thread instance. |
| Implementing Callable Interface | When the thread needs to return a result or throw a checked exception. | Supports returning values and handling exceptions. | More complex than Runnable due to Future and ExecutorService usage. |
| Using ThreadPool (ExecutorService) | When managing multiple threads efficiently is needed. | Manages resources efficiently, improves performance. | Requires careful management to avoid resource leaks. |
| Using Fork/Join Framework | When dealing with large recursive tasks that can be broken down into subtasks. | Optimized for parallel processing, improves performance on multi-core CPUs. | More complex to implement, best suited for divide-and-conquer problems. |
| Using Virtual Threads (Project Loom – Java 19+) | When a large number of lightweight threads are needed for high concurrency. | Low resource overhead, simplifies concurrent programming. | Not yet widely adopted, still evolving. |
This table provides an overview of various thread creation approaches in Java, helping developers choose the best method based on their use case.