Skip to main content

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

MethodDescriptionProsCons
Implementing RunnableUse with Runnable interface to define task in run() method.Simple to implement, can separate task from thread.Requires passing to Thread class.
Extending ThreadExtend Thread class and override run() method.Directly associated with thread, easy to start.Not recommended for reusability; limits inheritance.
Lambda with RunnableUse lambda expression with Runnable for concise syntax.Reduces boilerplate code.Limited to Java 8 and above.
ExecutorServiceManages threads, offers pooling, and handles shutdown.Great for managing multiple threads and complex tasks.Adds slight overhead; requires shutdown management.

Summary

  • Runnable Interface: Implementing Runnable is a common way to create threads, providing flexibility and allowing task and thread separation.
  • Thread Class: Extending Thread is a straightforward approach but limits inheritance and reusability.
  • Lambda Expressions: Lambdas streamline Runnable syntax, 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.