Java > Concurrency and Multithreading > Thread Basics > Creating Threads: Thread Class vs Runnable Interface
Creating Threads: Thread Class vs Runnable Interface
This example demonstrates two fundamental ways to create threads in Java: extending the Thread
class and implementing the Runnable
interface. Understanding the differences and advantages of each approach is crucial for effective multithreaded programming.
Extending the Thread Class
This code defines a class MyThread
that extends the Thread
class. The run()
method contains the code that will be executed in the new thread. The start()
method is invoked to begin the thread's execution. Each thread will print a message to the console indicating it is running. Extending the Thread
class is straightforward but limits inheritance options since Java only supports single inheritance.
class MyThread extends Thread {
@Override
public void run() {
System.out.println("Thread " + Thread.currentThread().getName() + " running (Thread Class).");
}
public static void main(String[] args) {
MyThread thread1 = new MyThread();
MyThread thread2 = new MyThread();
thread1.start();
thread2.start();
}
}
Implementing the Runnable Interface
This code defines a class MyRunnable
that implements the Runnable
interface. The run()
method, as before, contains the code executed by the thread. To start the thread, we create a Thread
object and pass an instance of MyRunnable
to its constructor. This approach promotes loose coupling and allows your class to inherit from another class, avoiding the single inheritance limitation. It is also more flexible since multiple threads can share the same Runnable
object.
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Thread " + Thread.currentThread().getName() + " running (Runnable Interface).");
}
public static void main(String[] args) {
MyRunnable runnable1 = new MyRunnable();
MyRunnable runnable2 = new MyRunnable();
Thread thread1 = new Thread(runnable1);
Thread thread2 = new Thread(runnable2);
thread1.start();
thread2.start();
}
}
Concepts Behind the Snippets
The core concept is concurrent execution. Threads allow multiple tasks to run seemingly simultaneously within a single program. This is achieved by time-slicing, where the CPU rapidly switches between different threads. The Thread
class provides the basic functionality for creating and managing threads, while the Runnable
interface provides a way to define the task that a thread will execute.
Real-Life Use Case Section
Downloading multiple files simultaneously. Instead of downloading files sequentially, you can create a thread for each file, significantly reducing the overall download time. Another example is handling multiple client requests in a server application. Each request can be processed by a separate thread, allowing the server to handle multiple clients concurrently.
Best Practices
Runnable
over extending Thread
for greater flexibility and adherence to object-oriented principles.run()
method to prevent unexpected thread termination.ExecutorService
for managing thread pools and simplifying thread management.
Interview Tip
Be prepared to explain the differences between extending Thread
and implementing Runnable
, including the advantages and disadvantages of each. Also, be ready to discuss potential problems like race conditions and deadlocks in multithreaded programs, and how to prevent them.
When to Use Them
Thread
when your class solely needs to be a thread and doesn't require inheriting behavior from another class. This is less common.Runnable
when your class already extends another class or needs to implement other interfaces. This is the more common and preferred approach. Also use Runnable
when multiple threads need to share the same task logic.
Memory Footprint
Each thread consumes memory for its stack, which stores local variables and method call information. Creating too many threads can lead to excessive memory consumption and performance degradation. Using thread pools can help manage memory consumption by reusing threads instead of creating new ones for each task.
Alternatives
ExecutorService
: A higher-level abstraction for managing thread pools.ForkJoinPool
: Designed for parallelizing recursive algorithms.
Pros of Runnable Interface
Runnable
object.
Cons of Extending Thread Class
FAQ
-
What is the difference between
start()
andrun()
?
Thestart()
method creates a new thread and then calls therun()
method in that new thread. Callingrun()
directly simply executes the code within the current thread; it does not create a new thread. -
Why should I prefer
Runnable
overThread
?
ImplementingRunnable
promotes better object-oriented design by separating the task to be executed from the thread management. It also allows your class to inherit from another class, which is not possible when extendingThread
. -
How do I pass data to a thread?
When implementingRunnable
, you can pass data to the thread through the constructor of theRunnable
class or by setting fields of theRunnable
object before passing it to theThread
constructor. When extendingThread
, you can create fields in yourThread
subclass and set them before callingstart()
.