Welcome back! In the previous lesson, we explored how to manage threads using Executors
and Runnable
. Now, we'll build on that foundation by introducing two advanced tools in Java's concurrency toolkit: Callable and Future. These components allow tasks to return results and handle exceptions, offering greater flexibility than Runnable
. By the end of this lesson, you'll be able to handle asynchronous computations more effectively in your Java applications.
In this lesson, you'll gain an understanding of how to:
- Use the
Future
interface to manage and retrieve results from asynchronous tasks. - Implement the
Callable
interface for tasks that return values or throw exceptions. - Submit
Callable
tasks to anExecutorService
and retrieve results usingFuture
.
These concepts will greatly enhance your ability to handle asynchronous operations.
The Future
interface represents the result of an asynchronous computation. When you submit a task to an ExecutorService
, it returns a Future
object, which serves as a placeholder for the task's result. The Future
provides several methods to monitor the status of the task, retrieve its result, and even cancel the task if necessary.
Let’s now look at an example that demonstrates submitting a Runnable
task and monitoring its status using Future
.
Java1Future<?> future = executor.submit(() -> { 2 System.out.println("Task executed by " + Thread.currentThread().getName()); 3}); 4 5if (!future.isDone()) { 6 System.out.println("Task is still running..."); 7} 8 9executor.shutdown();
In this example:
-
The task is submitted using the
submit()
method, which returns aFuture
object. Unlikeexecute()
, which does not return anything,submit()
gives you more control over the task, allowing you to track its progress and retrieve a result if needed. -
We use the
isDone()
method to check whether the task has completed execution. If the task is still running, it prints a message.future.isDone()
checks if aFuture
task is complete without blocking the thread, unlikefuture.get()
, which waits until the task finishes. It allows you to poll the task's status and act accordingly.
The Future
object provides additional flexibility compared to execute()
because it allows you to manage the task more effectively, especially when it involves results or long-running operations.
The Callable
interface is similar to Runnable
but with one major difference: it can return a result and throw exceptions. This makes Callable
ideal for tasks that perform calculations, data retrieval, or any other operation that needs to return a value.
While Runnable
tasks execute but don’t return anything, Callable
tasks return a value that you can retrieve using the Future
object.
Here’s an example of how to submit a Callable
task and retrieve the result:
Java1Callable<Integer> task = () -> { 2 System.out.println("Task executed by " + Thread.currentThread().getName()); 3 return 42; // Return result from the task 4}; 5 6Future<Integer> future = executor.submit(task); 7Integer result = future.get(); 8System.out.println("Result: " + result);
In this example:
-
A
Callable
task is defined to return the number42
. -
The task is submitted to the
ExecutorService
usingsubmit()
, which returns aFuture<Integer>
since the task produces anInteger
result. -
The
get()
method is used to retrieve the result of the task. This method blocks the current thread until the task is completed and the result is available.
Callable tasks enable you to perform computations asynchronously and return values directly, making them a valuable tool for more complex operations.
Both Runnable
and Callable
are used to define tasks that can be executed by a thread or submitted to an ExecutorService
. However, they serve different purposes:
-
Runnable: Used when you want to execute a task that doesn't return any result. It is suitable for simple tasks where you don’t need feedback.
-
Callable: Used when you need to execute a task that returns a result or may throw an exception. It is more flexible and powerful than
Runnable
but also requires the overhead of handlingFuture
objects and managing task results.
Now that we've explored both Callable
and Future
, let's move forward to practical exercises to see these concepts in action!