Table of contents
1.
Introduction
2.
Syntax
3.
Example of Java Future
4.
Methods of the Future Interface
5.
Using Future in Asynchronous Programming
6.
Getting Result
7.
Cancel an Asynchronous Task
8.
Check if an Asynchronous Task is Done
9.
Check if an Asynchronous Task is Cancelled
10.
Example of Java Future
10.1.
Java
11.
Frequently Asked Questions
11.1.
What happens if I call get() on a Future and the task hasn't completed yet?
11.2.
Can I cancel a task that has already completed?
11.3.
What's the difference between isDone() and isCancelled()?
12.
Conclusion
Last Updated: Jul 30, 2024
Easy

Java Future Interface

Author Riya Singh
0 upvote
Career growth poll
Do you think IIT Guwahati certified course can help you in your career?

Introduction

Java Future is a powerful tool that allows developers to write asynchronous code in a more manageable way. It represents the result of an asynchronous computation, which may not be available immediately. With the help of Java Future, you can start a task in the background and continue with other work while the task is running. Once the task is done, you can get the result using the Future object. This makes it easy to write code that can handle long-running operations without blocking the main thread. 

Java Future Interface

In this article, we will look at the basics of Java Future, like its syntax, methods, and how to use it in asynchronous programming.

Syntax

To use Java Future, you first need to understand its syntax. Here's a basic example of how to create a Future object:

Future<String> future = executor.submit(() -> {
    // Perform some task
    return "Task result";
});


In this example, we use an `ExecutorService` to submit a task that returns a string value. The `submit()` method returns a `Future<String>` object, which we can use to get the result of the task later.

The task itself is defined using a lambda expression `() -> { ... }`, which is a concise way to write an anonymous function in Java. You can also use a regular method or a `Callable` object to define the task.

Example of Java Future

ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(() -> {
    int result = 0;
    for (int i = 0; i < 10; i++) {
        result += i;
    }
    return result;
});
// Do some other work
System.out.println("Doing other work...");
// Get the result of the task
try {
    int result = future.get();
    System.out.println("Task result: " + result);
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}
executor.shutdown();


In this example, we create an `ExecutorService` using the `Executors.newSingleThreadExecutor()` method. This creates a single-threaded executor, which means that tasks will be executed one at a time.

We then submit a task to the executor using the `submit()` method. The task calculates the sum of the numbers from 0 to 9 and returns the result as an `Integer`.

While the task is running, we can do some other work in the main thread. In this case, we just print a message to the console.

To get the result of the task, we call the `get()` method on the `Future` object. This will block until the task is done and return the result. If the task throws an exception, `get()` will throw an `ExecutionException`.

Finally, we shut down the executor using the `shutdown()` method to release any resources it was using.

Methods of the Future Interface

The `Future` interface provides several methods that allow you to manage and retrieve the result of an asynchronous task. Here are some of the most commonly used methods:

1. `get()`: This method retrieves the result of the task. If the task is not yet complete, it will block until the task is done. If the task throws an exception, `get()` will throw an `ExecutionException`.
 

2. `get(long timeout, TimeUnit unit)`: This method is similar to `get()`, but it allows you to specify a timeout value. If the task does not complete within the specified time, a `TimeoutException` will be thrown.
 

3. `cancel(boolean mayInterruptIfRunning)`: This method attempts to cancel the task. If the task has not yet started, it will not be executed. If the task is already running, the `mayInterruptIfRunning` parameter determines whether the thread executing the task should be interrupted.
 

4. `isCancelled()`: This method returns `true` if the task was cancelled before it completed normally.
 

5. `isDone()`: This method returns `true` if the task has completed, either normally or by being cancelled.


Let’s discuss an example of how to use some of these methods:


Future<String> future = executor.submit(() -> {
    // Perform some task
    return "Task result";
});
// Check if the task is done
if (future.isDone()) {
    System.out.println("Task is already done!");
} else {
    System.out.println("Task is still running...");
}
// Try to retrieve the result, waiting up to 5 seconds
try {
    String result = future.get(5, TimeUnit.SECONDS);
    System.out.println("Task result: " + result);
} catch (TimeoutException e) {
    System.out.println("Task timed out!");
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}


In this example, we first check if the task is done using the `isDone()` method. If it is, we print a message saying so. If not, we print a message saying the task is still running.

We then try to retrieve the result of the task using the `get(long timeout, TimeUnit unit)` method, waiting up to 5 seconds for the task to complete. If the task completes within that time, we print the result. If the task times out, we print a message saying so. If the task throws an exception, it will be caught and printed.

Using Future in Asynchronous Programming

Java Future is a powerful tool for writing asynchronous code. It allows you to start a task in the background and continue with other work while the task is running. This can greatly improve the performance and responsiveness of your application.

Let’s see an example of Java Future in an asynchronous application:

public class AsyncExample {
    private ExecutorService executor = Executors.newFixedThreadPool(10);
    public Future<String> fetchData(String url) {
        return executor.submit(() -> {
            // Fetch data from the URL
            String data = // ...
            return data;
        });
    }
    public void processData(List<String> urls) {
        List<Future<String>> futures = new ArrayList<>();
        // Start a task for each URL
        for (String url : urls) {
            futures.add(fetchData(url));
        }
        // Process the results as they become available
        for (Future<String> future : futures) {
            try {
                String data = future.get();
                // Process the data
                // ...
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }
    }
}


In this example, we have an `AsyncExample` class that fetches data from a list of URLs asynchronously.

The `fetchData()` method takes a URL and returns a `Future<String>` that represents the result of fetching the data from that URL. The actual fetching is done in a background task submitted to an `ExecutorService`.

The `processData()` method takes a list of URLs and fetches the data for each one asynchronously. It does this by calling `fetchData()` for each URL and storing the resulting `Future` objects in a list.

It then processes the results as they become available by calling `get()` on each `Future`. This will block until the result is ready, but since we have multiple tasks running in the background, the overall processing can happen in parallel.

Getting Result

One of the key features of Java Future is the ability to get the result of an asynchronous task. The `get()` method is used for this purpose.

For example : 

ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(() -> {
    // Simulate a long-running task
    Thread.sleep(2000);
    return 42;
});
// Do some other work while the task is running
System.out.println("Doing other work...");
// Get the result of the task
try {
    Integer result = future.get();
    System.out.println("Task result: " + result);
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}
executor.shutdown();


In this example, we submit a task to the executor that simulates a long-running operation by sleeping for 2 seconds (using `Thread.sleep(2000)`) and then returning the value `42`.

After submitting the task, we can continue doing other work in the main thread. In this case, we just print a message to the console.

To get the result of the task, we call `future.get()`. This will block until the task is complete and then return the result (in this case, `42`).

If the task throws an exception, `get()` will throw an `ExecutionException`. If the thread waiting for the result is interrupted, `get()` will throw an `InterruptedException`. It's important to handle these exceptions appropriately.

Note that calling `get()` will block indefinitely if the task never completes. If you want to avoid this, you can use the `get(long timeout, TimeUnit unit)` method to specify a maximum time to wait for the result.

For example:

try {
    Integer result = future.get(5, TimeUnit.SECONDS);
    System.out.println("Task result: " + result);
} catch (TimeoutException e) {
    System.out.println("Task timed out!");
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}


In this case, if the task doesn't complete within 5 seconds, a `TimeoutException` will be thrown.

Cancel an Asynchronous Task

Sometimes you may need to cancel an asynchronous task before it completes. Java Future provides the `cancel()` method for this purpose.

For example:

ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(() -> {
    int result = 0;
    for (int i = 0; i < 100; i++) {
        result += i;
        Thread.sleep(100);
    }
    return result;
});
// Cancel the task after 1 second
Thread.sleep(1000);
future.cancel(true);
// Check if the task was cancelled
if (future.isCancelled()) {
    System.out.println("Task was cancelled");
} else {
    System.out.println("Task completed normally");
}
executor.shutdown();


In this example, we submit a task to the executor that sums the numbers from 0 to 99, sleeping for 100 milliseconds between each addition. This simulates a long-running task that can be interrupted.

After submitting the task, we sleep the main thread for 1 second and then call `future.cancel(true)`. The `true` argument means that the task should be interrupted if it's currently running.

We then check if the task was cancelled using the `isCancelled()` method. In this case, since we cancelled the task after 1 second, it's likely that the task was indeed cancelled and didn't complete normally.

It's important to note that `cancel()` doesn't guarantee that the task will stop immediately. If the task is not responsive to interruption (for example, if it's in a blocking operation that doesn't check for interruption), it may continue running even after `cancel()` is called.

Also, if the task has already completed by the time `cancel()` is called, the cancellation request will have no effect.

After cancelling a task, calling `get()` on the `Future` will throw a `CancellationException`.

For example:

try {
    Integer result = future.get();
} catch (CancellationException e) {
    System.out.println("Task was cancelled");
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}

Check if an Asynchronous Task is Done

Sometimes you may want to check if an asynchronous task has completed without blocking to wait for its result. Java Future provides the `isDone()` method for this purpose.

For example : 

ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(() -> {
    // Simulate a long-running task
    Thread.sleep(2000);
    return 42;
});
// Check if the task is done
if (future.isDone()) {
    System.out.println("Task has completed");
} else {
    System.out.println("Task is still running");
}
// Do some other work
System.out.println("Doing other work...");
// Check again if the task is done
if (future.isDone()) {
    System.out.println("Task has completed");
    try {
        Integer result = future.get();
        System.out.println("Task result: " + result);
    } catch (InterruptedException | ExecutionException e) {
        e.printStackTrace();
    }
} else {
    System.out.println("Task is still running");
}
executor.shutdown();


In this example, we submit a task to the executor that sleeps for 2 seconds (simulating a long-running operation) and then returns the value `42`.

Immediately after submitting the task, we check if it's done using `future.isDone()`. Since the task takes 2 seconds to complete, it's very likely that it won't be done at this point, so we print "Task is still running".

We then do some other work (in this case, just print a message), and then check again if the task is done. This time, it's likely that the 2 seconds have passed and the task has completed, so we print "Task has completed" and get the result of the task using `future.get()`.

The `isDone()` method returns `true` if the task has completed, whether it completed normally, threw an exception, or was cancelled. It returns `false` if the task is still running.

Note that `isDone()` does not block. It returns immediately with the current status of the task.

This method is useful when you want to do something based on the completion of the task, but you don't want to block your main thread to wait for the result.

For example, you might use `isDone()` to provide a progress indicator to the user, or to start a new task when a previous one has completed.

Check if an Asynchronous Task is Cancelled

In addition to checking if a task is done, you can also specifically check if a task was cancelled using the `isCancelled()` method provided by Java Future.

For example : 

ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(() -> {
    int result = 0;
    for (int i = 0; i < 100; i++) {
        result += i;
        Thread.sleep(100);
    }
    return result;
});
// Cancel the task after 1 second
Thread.sleep(1000);
future.cancel(true);
// Check if the task was cancelled
if (future.isCancelled()) {
    System.out.println("Task was cancelled");
} else {
    System.out.println("Task was not cancelled");
}
// Attempting to get the result will throw a CancellationException
try {
    Integer result = future.get();
} catch (CancellationException e) {
    System.out.println("Attempting to get the result of a cancelled task throws a CancellationException");
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}
executor.shutdown();


This example is similar to the one in the "Cancel an Asynchronous Task" section. We submit a task to the executor, then cancel it after 1 second.

We then use `future.isCancelled()` to check if the task was indeed cancelled. This method returns `true` if the task was cancelled before it completed normally.

Note that `isCancelled()` will return `true` even if the task was cancelled after it had already completed. In other words, it doesn't tell you whether the cancellation actually had any effect, only that `cancel()` was called at some point.

After a task has been cancelled, attempting to `get()` its result will throw a `CancellationException`. This is demonstrated in the example above.

The `isCancelled()` method is useful when you need to take different actions based on whether a task completed normally or was cancelled. For example, you might use it to clean up resources that are no longer needed if a task was cancelled.

Example of Java Future

  • Java

Java

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

public class FutureExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(2);

// Submit a task and get its Future
Future<Integer> future1 = executor.submit(new Callable<Integer>() {
@Override
public Integer call() throws InterruptedException {
int sum = 0;
for (int i = 0; i < 100; i++) {
sum += i;
Thread.sleep(10);
}
return sum;
}
});

// Submit another task and get its Future
Future<Double> future2 = executor.submit(new Callable<Double>() {
@Override
public Double call() throws InterruptedException {
double sum = 0;
for (double i = 0; i < 100; i++) {
sum += i;
Thread.sleep(10);
}
return sum;
}
});

// Check if the tasks are done
while (!future1.isDone() || !future2.isDone()) {
System.out.println("Tasks are not done yet...");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

// Get the results of the tasks
try {
Integer result1 = future1.get();
Double result2 = future2.get();
System.out.println("Task 1 result: " + result1);
System.out.println("Task 2 result: " + result2);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}

// Submit a task and cancel it
Future<String> future3 = executor.submit(new Callable<String>() {
@Override
public String call() throws InterruptedException {
Thread.sleep(2000);
return "This task was cancelled";
}
});
future3.cancel(true);

// Check if the task was cancelled
if (future3.isCancelled()) {
System.out.println("Task 3 was cancelled");
} else {
try {
String result3 = future3.get();
System.out.println("Task 3 result: " + result3);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}

executor.shutdown();
}
}
You can also try this code with Online Java Compiler
Run Code


Output

Tasks are not done yet...
Tasks are not done yet...
Tasks are not done yet...
Tasks are not done yet...
Tasks are not done yet...
Tasks are not done yet...
Tasks are not done yet...
Tasks are not done yet...
Tasks are not done yet...
Tasks are not done yet...
Tasks are not done yet...
Tasks are not done yet...
Tasks are not done yet...
Tasks are not done yet...
Tasks are not done yet...
Tasks are not done yet...
Tasks are not done yet...
Tasks are not done yet...
Task 1 result: 4950
Task 2 result: 4950.0
Task 3 was cancelled


In this example, we create an `ExecutorService` with a fixed thread pool of size 2.

We then submit two tasks to the executor, each of which calculates a sum in a slightly different way. We get the `Future` objects for these tasks.

We use a while loop to check if the tasks are done using the `isDone()` method. The loop keeps running until both tasks are done. Inside the loop, we print a message and sleep for a short time to avoid excessive CPU usage.

Once both tasks are done, we get their results using the `get()` method and print them.

We then submit a third task and immediately cancel it using `cancel(true)`. We check if the task was cancelled using `isCancelled()` and print a message if it was.

Finally, we shut down the executor.

This example showcases several key features of Java Future:
 

- Submitting tasks to an executor and getting their `Future` objects.
 

- Checking if tasks are done using `isDone()`.
 

- Getting the results of tasks using `get()`.
 

- Cancelling a task using `cancel()`.
 

- Checking if a task was cancelled using `isCancelled()`.

Frequently Asked Questions

What happens if I call get() on a Future and the task hasn't completed yet?

The get() method will block until the task completes, and then it will return the result.

Can I cancel a task that has already completed?

Yes, you can call cancel() on a Future even if the task has already completed, but it will have no effect.

What's the difference between isDone() and isCancelled()?

isDone() returns true if the task has completed, whether normally, by throwing an exception, or by being cancelled. isCancelled() returns true only if the task was cancelled before it completed normally.

Conclusion

In this article, we have learned about Java Future, a powerful tool for managing asynchronous tasks. We've seen how to submit tasks to an executor, get their Future objects, check if tasks are done, get task results, and cancel tasks. We've also looked at several methods provided by the Future interface, such as get(), isDone(), isCancelled(), and cancel(). Java Future allows you to write more efficient and responsive asynchronous code, especially when dealing with time-consuming I/O operations. 

You can also practice coding questions commonly asked in interviews on Coding Ninjas Code360

Also, check out some of the Guided Paths on topics such as Data Structure and AlgorithmsCompetitive ProgrammingOperating SystemsComputer Networks, DBMSSystem Design, etc., as well as some Contests, Test Series, and Interview Experiences curated by top Industry Experts.

Live masterclass