Java tutorials > Multithreading and Concurrency > Threads and Synchronization > How to create threads (Thread vs Runnable)?
How to create threads (Thread vs Runnable)?
In Java, multithreading allows concurrent execution of multiple parts of a program. Creating threads is a fundamental aspect of utilizing multithreading capabilities. There are two primary ways to create threads in Java: by extending the Thread class or by implementing the Runnable interface. Each approach has its own advantages and disadvantages. This tutorial will walk you through both methods with clear examples.
Creating Threads by Extending the Thread Class
This method involves creating a new class that inherits from the Thread class. You then override the run() method to define the task that the thread will execute. The start() method is crucial; it initiates the thread and calls the run() method in a new thread of execution. Calling run() directly simply executes the code within the current thread, defeating the purpose of multithreading. Using Thread.currentThread().getName() provides insight into which thread is currently running.
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("Thread running: " + Thread.currentThread().getName());
}
public static void main(String[] args) {
MyThread thread1 = new MyThread();
thread1.start(); // Starts the thread and calls the run() method
MyThread thread2 = new MyThread();
thread2.start();
}
}
Creating Threads by Implementing the Runnable Interface
This approach involves implementing the Runnable interface. You create a class that implements Runnable and provides a run() method defining the task for the thread. Then, you create a new Thread object, passing your Runnable implementation as an argument to the Thread constructor. Finally, you call start() on the Thread object to begin execution in a separate thread. This approach promotes better separation of concerns and allows your class to inherit from another class if needed.
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Thread running: " + Thread.currentThread().getName());
}
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread1 = new Thread(myRunnable);
thread1.start();
Thread thread2 = new Thread(myRunnable);
thread2.start();
}
}
Concepts Behind the Snippets
Extending Thread: This approach directly couples the thread's behavior with the class definition. It's straightforward but less flexible as Java doesn't allow multiple inheritance. Your class becomes a specialized type of Thread. Implementing Runnable: This is more flexible. You define the task to be executed in the run() method, and then create a Thread object to execute that task. This decoupling is beneficial because your class can extend another class, adhering to the principle of single responsibility and facilitating better code organization.
Real-Life Use Case
Imagine a server application handling multiple client requests concurrently. Each request can be processed in a separate thread. Using Runnable is preferred in such scenarios because the request handler class might need to extend another class related to networking or data processing. For example, a web server might use a thread pool where each thread executes a Runnable task representing a client request.
Best Practices
Runnable: In most cases, implementing Runnable is the preferred approach because it provides greater flexibility and promotes better code organization.ExecutorService, which manages thread pools and simplifies thread management.run() method are properly handled to prevent thread termination and potential data corruption.synchronized blocks or Lock objects to prevent race conditions and ensure data consistency.
Interview Tip
Be prepared to discuss the differences between extending Thread and implementing Runnable, including the benefits of each approach. Also, be ready to explain scenarios where one approach is more appropriate than the other. Understanding thread lifecycle and synchronization is crucial.
When to use them
Thread does not conflict with other design requirements. It is more straightforward for simple cases.
Memory footprint
The memory footprint of a thread depends on various factors like the stack size and the JVM implementation. There's no significant difference in memory footprint between a thread created by extending Thread and a thread created using Runnable. The memory used by the Thread object itself is relatively small.
Alternatives
Alternatives to creating threads directly include using ExecutorService for managing thread pools, using ForkJoinPool for divide-and-conquer algorithms, and using reactive programming libraries like RxJava or Project Reactor for asynchronous and event-driven programming.
Pros and Cons of Extending Thread
Pros:
Cons:
- Limits inheritance capabilities, as Java only allows single inheritance.
- Tightly couples the task to the thread, reducing flexibility.
Pros and Cons of Implementing Runnable
Pros:
Cons:
- Slightly more verbose compared to extending
Thread.
FAQ
-
What happens if I call the
run()method directly instead ofstart()?
Calling therun()method directly will execute the code within the current thread, not in a new thread. It will behave like a regular method call, defeating the purpose of multithreading. -
Can I reuse a
Runnableobject with multiple threads?
Yes, you can create multipleThreadobjects using the sameRunnableinstance. EachThreadwill execute therun()method of theRunnableobject concurrently. -
What is the difference between
Thread.sleep()andThread.yield()?
Thread.sleep()pauses the execution of the current thread for a specified amount of time. The thread goes into a waiting state.Thread.yield()hints to the scheduler that the current thread is willing to relinquish its use of the CPU. The scheduler is free to ignore this hint.