Key differences between Thread Class and Runnable Interface in Java

Thread Class in Java

In Java, the Thread class is a fundamental part of the java.lang package, pivotal for creating and managing new threads. A thread in Java is a path of execution within a program, allowing for concurrent execution of code. Threads run in parallel, making them ideal for performing time-consuming operations without blocking the main application’s user interface or running multiple operations simultaneously, improving the application’s performance.

Thread class provides constructors and methods to create and start a new thread, set the thread’s name, its priority, and handle its execution. Users can create a new thread by either extending the Thread class directly and overriding its run() method or by implementing the Runnable interface in an object and passing it to a Thread instance. Utilizing threads effectively can help in creating responsive and efficient applications.

Functions of Thread Class in Java:

  • Thread Creation:

You can create a new thread by instantiating the Thread class using one of its constructors. Threads can be created by extending the Thread class itself or by passing a Runnable object to the Thread constructor.

  • Starting a Thread:

start() method of the Thread class is used to start a newly created thread. Once started, the Java Virtual Machine calls the run() method of that thread.

  • Thread Execution:

run() method is where the actual code to be executed by the thread is defined. By overriding this method, you can specify the thread’s behavior.

  • Setting Thread Priority:

setPriority(int) method is used to set the priority of a thread. Thread priorities are used by the thread scheduler to decide when each thread should be allowed to run.

  • Sleeping:

sleep(long millis) static method can be used to pause the execution of the current thread for a specified period (in milliseconds). This is often used in managing timing and delays in threads.

  • Thread Interruption:

interrupt() method is used to interrupt a thread that is waiting, sleeping, or otherwise occupied, causing it to immediately throw an InterruptedException.

  • Checking Thread Status:

Thread class provides methods like isAlive() which checks if the thread is still running, and isInterrupted() to check if a thread has been interrupted.

  • Thread Joining:

join() method allows one thread to wait for the completion of another. This is useful when a thread must wait until another thread finishes processing before continuing.

Example of Thread Class in Java:

This example will create a class that extends Thread and overrides its run() method to perform an action. Additionally, I will provide an example of implementing the Runnable interface, as both methods are commonly used.

Example 1: Extending the Thread class

// Create a class that extends Thread

class MyThread extends Thread {

    public void run() {

        System.out.println(“MyThread is running.”);

        for (int i = 0; i < 5; i++) {

            try {

                Thread.sleep(1000); // Pause for 1 second

                System.out.println(“Count: ” + i);

            } catch (InterruptedException e) {

                System.out.println(“Thread was interrupted.”);

            }

        }

        System.out.println(“MyThread has finished executing.”);

    }

}

public class Main {

    public static void main(String[] args) {

        MyThread t = new MyThread(); // Create an instance of MyThread

        t.start(); // Start the thread

    }

}

Example 2: Implementing the Runnable Interface

// Create a class that implements Runnable

class MyRunnable implements Runnable {

    public void run() {

        System.out.println(“Runnable is running.”);

        for (int i = 0; i < 5; i++) {

            try {

                Thread.sleep(1000); // Pause for 1 second

                System.out.println(“Count: ” + i);

            } catch (InterruptedException e) {

                System.out.println(“Runnable was interrupted.”);

            }

        }

        System.out.println(“Runnable has finished executing.”);

    }

}

public class Main {

    public static void main(String[] args) {

        MyRunnable myRunnable = new MyRunnable(); // Create an instance of MyRunnable

        Thread t = new Thread(myRunnable); // Pass MyRunnable instance to a Thread

        t.start(); // Start the thread

    }

}

Explanation

  • Extending Thread:

In the first example, we directly extend Thread and override the run() method. We then create an instance of this subclass and start the thread using the start() method.

  • Implementing Runnable:

In the second example, we define a class that implements Runnable. We then create an instance of Thread, pass our Runnable instance to it, and start the thread. This method is more flexible and allows the task to be run by different executors.

Runnable Interface in Java

In Java, the Runnable interface is a functional interface used to represent a task that can be executed by a thread. It contains a single method, run(), that needs to be implemented by any class whose instances are intended to be executed by a thread. The Runnable interface is designed to provide a means to separate a task’s logic from the mechanism of running it concurrently in threads. By implementing Runnable, a class ensures that its instances can be passed to a Thread object, which can then execute the run() method in a new thread of control. This separation allows for greater flexibility and reusability of thread logic and is fundamental in Java’s approach to multithreading and concurrent programming.

Functions of Runnable Interface in Java:

  • Decoupling Task Definition from Execution:

Runnable allows the separation of the “what” from the “how” of thread execution. It defines a task to be executed without specifying the thread management details, enabling the same task to be executed by different thread managers or executors.

  • Thread Reusability:

By implementing Runnable, you can pass the same Runnable instance to multiple threads, making it easier to manage a set of similar tasks across different threads without duplicating code.

  • Use with Thread Pools:

Runnable tasks are commonly used with thread pools, where tasks are submitted in the form of Runnable objects to be executed by a pool of threads, optimizing resource utilization and improving application performance.

  • Flexibility in Task Execution:

Implementing Runnable provides flexibility in how a task is executed—whether directly through a Thread instance or by an ExecutorService, which can manage a larger set of threads more efficiently.

  • Scheduling and Timing Tasks:

Runnable can be used with scheduling utilities like ScheduledExecutorService to run tasks periodically or after a certain delay, providing a powerful mechanism for timed task execution.

  • Integration with Concurrency Utilities:

Runnable is compatible with advanced concurrency utilities in Java like Future and Callable (via adapters). This integration allows tasks implemented by Runnable to return results or throw exceptions, enhancing error handling and result retrieval.

  • Improving Application Responsiveness:

By offloading lengthy tasks to a separate thread using Runnable, you can keep the user interface responsive, which is crucial for maintaining a good user experience in GUI applications.

  • Facilitating Asynchronous Computation:

Runnable enables asynchronous computation, allowing applications to perform background operations while the main program continues its execution, thereby improving throughput and efficiency.

Example of Runnable Interface in Java:

This example will illustrate implementing the Runnable interface and using a Thread to execute it.

Example: Implementing the Runnable Interface

// Define a class that implements Runnable

class HelloRunnable implements Runnable {

    public void run() {

        System.out.println(“Hello from a thread!”);

        for (int i = 0; i < 5; i++) {

            try {

                Thread.sleep(1000); // Pause for 1 second

                System.out.println(“Thread: Counting ” + i);

            } catch (InterruptedException e) {

                System.out.println(“Thread was interrupted.”);

            }

        }

        System.out.println(“Thread has finished executing.”);

    }

}

public class RunnableExample {

    public static void main(String[] args) {

        // Create an instance of HelloRunnable

        HelloRunnable myRunnable = new HelloRunnable();

       

        // Create a Thread object using HelloRunnable

        Thread thread = new Thread(myRunnable);

       

        // Start the thread

        thread.start();

        // Main thread continues to execute

        for (int i = 0; i < 5; i++) {

            try {

                Thread.sleep(800); // Pause for 800 milliseconds

                System.out.println(“Main: Counting ” + i);

            } catch (InterruptedException e) {

                System.out.println(“Main thread was interrupted.”);

            }

        }

        System.out.println(“Main thread has finished executing.”);

    }

}

Explanation

  • Runnable Implementation:

The class HelloRunnable implements the Runnable interface by defining the run() method. This method contains the code that the thread will execute when started.

  • Thread Execution:

A new Thread object is created by passing the HelloRunnable instance to its constructor. This setup ties the Runnable task (the run() method’s logic) to the Thread.

  • Starting the Thread:

start() method on the Thread instance is invoked to begin execution of the Runnable task in a new thread. This method is crucial as it triggers the run() method to execute in parallel to the main thread.

  • Concurrency Demonstration:

The main thread continues executing its own loop concurrently, demonstrating how two blocks of code (the main thread’s loop and the Runnable thread’s loop) run simultaneously. The differing sleep times (800 milliseconds for the main thread and 1000 milliseconds for the new thread) highlight the asynchronous nature of their operations.

Key differences between Thread Class and Runnable Interface in Java

Aspect Thread Class Runnable Interface
Type Class Interface
Inheritance Extends Thread class Implements Runnable interface
Multiple Inheritance Not possible Possible with other interfaces
Method Required Override run() Implement run()
Constructor Various constructors available None needed
Flexibility Less flexible More flexible
Reuse Hard to reuse with others Easy to reuse
Combining Tasks One class, one task Multiple tasks possible
Object Type Is a thread Is a task
Thread Management Direct control Indirect control
Usage with Pools Direct usage not optimal Ideal for thread pools
Resource Utilization Independent resources Shared resources
Design Pattern Subclassing Delegation
Functionality Provides more than run() Single functional interface
API Integration Specific to thread Works with broader APIs

Key Similarities between Thread Class and Runnable Interface in Java

  • Purpose:

Both are used for achieving multithreading in Java. They provide mechanisms to run code in parallel to the main execution thread of an application.

  • Method run():

Both require the definition of a run() method that contains the code to be executed by the thread. In the Thread class, this method can be overridden, while in the Runnable interface, it must be implemented.

  • Usage with Thread constructor:

Instances of both Thread itself and classes implementing Runnable can be passed to a Thread constructor. This is crucial for starting a new thread regardless of whether Thread is extended or Runnable is implemented.

  • Starting a Thread:

The way to start a thread is similar in both cases—by calling the start() method on a Thread object, which internally calls the run() method of the Thread or Runnable instance.

  • Thread Lifecycle Management:

Both mechanisms use the same thread lifecycle, including states like New, Runnable, Blocked, Waiting, Timed Waiting, and Terminated. The lifecycle management functions (start(), sleep(), join(), etc.) are handled similarly.

  • Concurrent Execution:

Both allow for concurrent execution of code, enabling applications to perform multiple operations simultaneously, thus improving performance and responsiveness.

  • Implementation of Runnable by Thread:

Thread class itself implements Runnable, indicating an inherent design similarity in how both are fundamentally integrated within Java’s threading model.

  • Core Java Feature:

Both are fundamental to Java and are part of the standard library (java.lang package), illustrating their essential role in Java’s design for handling concurrent operations.

Leave a Reply

error: Content is protected !!