DEV Community

Anh Trần Tuấn
Anh Trần Tuấn

Posted on • Originally published at tuanh.net on

Understanding Runnable and Callable in Java: Examples and Code Demos

1. Introduction to Runnable and Callable

Concurrency in Java is achieved through threads, and Runnable and Callable are two key interfaces used to define tasks that can be executed by threads.

1.1 What is Runnable?

Image

The Runnable interface represents a task that can be executed concurrently by a thread. It defines a single method, run(), which contains the code to be executed. Runnable does not return a result or throw a checked exception.

Example Code:

public class RunnableExample implements Runnable {
    @Override
    public void run() {
        System.out.println("Runnable is running!");
    }

    public static void main(String[] args) {
        RunnableExample example = new RunnableExample();
        Thread thread = new Thread(example);
        thread.start();
    }
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • RunnableExample implements the Runnable interface.
  • The run() method prints a message to the console.
  • In the main method, a Thread is created with RunnableExample and started. This initiates the run() method in a new thread.

1.2 What is Callable?

Image

The Callable interface is similar to Runnable but with a few differences. It represents a task that returns a result and can throw a checked exception. The call() method, which must be implemented, returns a result and can handle exceptions.

Example Code:

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class CallableExample implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "Callable has returned a result!";
    }

    public static void main(String[] args) {
        CallableExample example = new CallableExample();
        FutureTask<String> futureTask = new FutureTask<>(example);
        Thread thread = new Thread(futureTask);
        thread.start();

        try {
            System.out.println(futureTask.get());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • CallableExample implements the Callable interface with a generic type String.
  • The call() method returns a result.
  • FutureTask is used to wrap the Callable task. It allows for retrieving the result after the task is completed.

2. Key Differences Between Runnable and Callable

Understanding the differences between Runnable and Callable can help in selecting the right tool for different scenarios.

Image

Return Values

  • Runnable does not return a result. It is suitable for tasks where no result needs to be returned.
  • Callable returns a result, making it useful for tasks where a result is required.

Exception Handling

  • Runnable cannot throw checked exceptions. It can only handle runtime exceptions within its run() method.
  • Callable can throw checked exceptions. This makes it more versatile for tasks that may fail under certain conditions.

3. Practical Applications and Examples

Let’s explore practical scenarios where Runnable and Callable are commonly used.

3.1 Using Runnable for Background Tasks

Runnable is often used for background tasks that do not need to return a result, such as performing periodic updates or handling user interactions.

Example Code:

public class BackgroundTask implements Runnable {
    @Override
    public void run() {
        // Simulate background work
        for (int i = 0; i < 5; i++) {
            System.out.println("Task running: " + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        Thread backgroundThread = new Thread(new BackgroundTask());
        backgroundThread.start();
    }
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • BackgroundTask performs a repetitive action and sleeps for one second between iterations.
  • The main method starts the task in a new thread.

3.2 Using Callable for Tasks Requiring Results

Callable is ideal for tasks where you need a result or need to handle exceptions. For instance, when performing computations or retrieving data from a source.

Example Code:

import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

public class ComputationTask implements Callable<Integer> {
    @Override
    public Integer call() {
        // Simulate computation
        int sum = 0;
        for (int i = 0; i < 1000; i++) {
            sum += i;
        }
        return sum;
    }

    public static void main(String[] args) {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<Integer> future = executor.submit(new ComputationTask());

        try {
            System.out.println("Result of computation: " + future.get());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            executor.shutdown();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • ComputationTask performs a computation and returns the result.
  • ExecutorService is used to manage the task execution and retrieve the result through Future.

4. Conclusion

Both Runnable and Callable are essential for handling concurrent tasks in Java, each serving different purposes. Runnable is suitable for tasks without results, while Callable is preferred for tasks that need to return results and handle exceptions. Understanding their differences and applications can greatly enhance your concurrent programming skills.

If you have any questions or need further clarification, feel free to leave a comment below!

Read posts more at : Understanding Runnable and Callable in Java: Examples and Code Demos

Top comments (1)

Collapse
 
okom profile image
okom

Cool, This is good thank you