The Future Interface — The Best way for Asynchronous Java Programming

The Future Interface — The Best way for Asynchronous Java Programming

Asynchronous programming in Java is a technique for parallel programming that lets teams distribute work and build application features separately from the primary application thread. Then, when the team is ready with the feature, the code is synced with the main thread. The benefits of Asynchronous programming like improved application performance and enhanced responsiveness are known to all. There are multiple ways to do Async programming in Java, starting from Thread, Runnable, Callable<T>, Future<T> (and its extended ScheduledFuture<T>), CompletableFuture<T>, and of course, ExecutorService and ForkJoinPool.

Java concurrency is the functionality that enables asynchronous programming in Java. Concurrency is mainly the ability to run several programs or applications parallelly smoothly. The java.util.concurrent package is the best way to do asynchronous programming. It provides developers with all the tools required for creating concurrent applications in Java. The backbone of this package consists of threads.

This method of using the java.util.concurrent package for enabling concurrency is usually known as the Future Token method or the Java Future interface. Many built-in concurrency utilities of Java are known to return a Java Future object as output, e.g., the Java ExecutorService. The java.util.concurrent package is built on this functionality. A java.util.concurrent.Future represents the result of an asynchronous computation. When an asynchronous task is created, a Java Future object is returned. This Future object functions as a handle to the result of the asynchronous task. Once the asynchronous task completes, the result can be accessed via the Future object returned when the task was started.

To understand how the Java Future interface works, let us dive into the basics of the interface first.

The Java Future Interface

This is the skeleton of the Java Future interface. The code below is a clear explanation of how the Java Future interface works.

Javapublic interface Future<V>
{
V get();
V get(long timeout, TimeUnit unit);
boolean isCancelled();
boolean isDone();
boolean cancel(boolean mayInterruptIfRunning);
}

Asynchronous programming in Java is a technique for parallel programming that lets teams distribute work and build application features separately from the primary application thread. Then, when the team is ready with the feature, the code is synced with the main thread. The benefits of Asynchronous programming like improved application performance and enhanced responsiveness are known to all. There are multiple ways to do Async programming in Java, starting from Thread, Runnable, Callable<T>, Future<T> (and its extended ScheduledFuture<T>), CompletableFuture<T>, and of course, ExecutorService and ForkJoinPool.

Java concurrency is the functionality that enables asynchronous programming in Java. Concurrency is mainly the ability to run several programs or applications parallelly smoothly. The `java.util.concurrent` package is the best way to do asynchronous programming. It provides developers with all the tools required for creating concurrent applications in Java. The backbone of this package consists of threads.

This method of using the `java.util.concurrent` package for enabling concurrency is usually known as the Future Token method or the Java Future interface. Many built-in concurrency utilities of Java are known to return a Java Future object as output, e.g., the Java ExecutorService. The `java.util.concurrent` package is built on this functionality. A java.util.concurrent.Future represents the result of an asynchronous computation. When an asynchronous task is created, a Java Future object is returned. This Future object functions as a handle to the result of the asynchronous task. Once the asynchronous task completes, the result can be accessed via the Future object returned when the task was started.

To understand how the Java Future interface works, let us dive into the basics of the interface first.

The Java Future Interface

This is the skeleton of the Java Future interface. The code below is a clear explanation of how the Java Future interface works.

Javapublic interface Future<V>
{
V get();
V get(long timeout, TimeUnit unit);
boolean isCancelled();
boolean isDone();
boolean cancel(boolean mayInterruptIfRunning);
}

Asynchronous programming in Java is a technique for parallel programming that lets teams distribute work and build application features separately from the primary application thread. Then, when the team is ready with the feature, the code is synced with the main thread. The benefits of Asynchronous programming like improved application performance and enhanced responsiveness are known to all. There are multiple ways to do Async programming in Java, starting from Thread, Runnable, Callable<T>, Future<T> (and its extended ScheduledFuture<T>), CompletableFuture<T>, and of course, ExecutorService and ForkJoinPool.

Java concurrency is the functionality that enables asynchronous programming in Java. Concurrency is mainly the ability to run several programs or applications parallelly smoothly. The `java.util.concurrent` package is the best way to do asynchronous programming. It provides developers with all the tools required for creating concurrent applications in Java. The backbone of this package consists of threads.

This method of using the `java.util.concurrent` package for enabling concurrency is usually known as the Future Token method or the Java Future interface. Many built-in concurrency utilities of Java are known to return a Java Future object as output, e.g., the Java ExecutorService. The `java.util.concurrent` package is built on this functionality. A java.util.concurrent.Future represents the result of an asynchronous computation. When an asynchronous task is created, a Java Future object is returned. This Future object functions as a handle to the result of the asynchronous task. Once the asynchronous task completes, the result can be accessed via the Future object returned when the task was started.

To understand how the Java Future interface works, let us dive into the basics of the interface first.

The Java Future Interface

This is the skeleton of the Java Future interface. The code below is a clear explanation of how the Java Future interface works.

Javapublic interface Future<V>
{
V get();
V get(long timeout, TimeUnit unit);
boolean isCancelled();
boolean isDone();
boolean cancel(boolean mayInterruptIfRunning);
}

Create a Future

You can create a Java Future Token by submitting a task to an ExecutorService. Here, I’m creating a component and initializing an ExecutorService with available core capacity.

Javapublic class FutureTutorial
{
private ExecutorService executorService;
public FutureTutorial()
{
// It's a best practice utilize the maximum core capacity
// until there is a valid reason to use the lesser value
executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
}
}

The Future Interface is a generic interface. This means that the output will also display the type of the return data. Let’s understand it with a Callable function that returns an integer value.

With Callable<T>

Javapublic Future<Integer> submitCallable()
{
Callable<Integer> task = ()->
{
System.out.println("I'm a callable");
return 10;
};
Future<Integer> future = executorService.submit(task);
return future;
}

Here, We have created a callable task that returns an integer value. The return type of callable is also an integer. So, the generic type of the Future Token obtained by the submission of the callable task is also an integer.

With Runnable

We are all aware that a Callable function returns a value, but a Runnable function does not. So, what type of Future Token is obtained by submitting a Runnable?

As the return type is unknown, the executor returns Future<?>.

Javapublic Future<?> submitRunnable()
{
Runnable task = ()->System.out.println("I'm a runnable");
Future<?> future = executorService.submit(task);
return future;
}

Future.isDone()

Once we submit our task and get the Future token, how do we confirm that our task is complete?

Future.isDone() provides the status of the task. It will return ‘true’ value if this task has been completed. Completion may be due to normal termination, an exception, or cancellation — in all of these cases, this method will return a `true` value.

Javapublic Future<Integer> checkResult() throws InterruptedException
{
Future<Integer> future = submitCallable();
while (!future.isDone())
{
// spin lock to save the cpu cycles
Thread.sleep(1000);
}
return future;
}

Future.get()

Future.get() is an important function. It is used for getting the final result of the submitted task. But, it serves another purpose as well. If you have a runnable task and just want to check its completion status, you can also do it with the isDone() command. When your task does not return any data, this function works fine. But, when your task is completed with an exception and that exception is stored in the Future token, the only way to get it is Future.get().

If we modify our submitRunnable to produce an unhandled exception, the result will be:

Javapublic Future<?> submitRunnable()
{
Runnable task = ()->
{
System.out.println("I'm a runnable");
// This causes an ArithmeticException
int res = 10/0;
System.out.println("This will not be printed due to arithmetic exception.");
};
Future<?> future = executorService.submit(task);
return future;
}

And now I’m calling the checkResult()

Javapublic void checkResult() throws InterruptedException
{
Future<?> future = submitRunnable();
while (!future.isDone())
{
// spin lock to save the cpu cycles
Thread.sleep(1000);
}
System.out.println("Task completed.");
}

With no exceptions, It will complete smoothly. Unless we callFuture.get() we don’t see a trace of Arithmetic Exception. It will be a mystery!

Now you know the importance of Future.get(), Let’s see how to use it.

Javapublic Integer getResult()
{
Future<Integer> future = submitCallable();
try
{
return future.get();
}
catch (InterruptedException e)
{
log.error("You task has been interrupted", e);
}
catch (ExecutionException e)
{
log.error("You task has been completed exceptionally", e);
}
return Integer.MIN_VALUE;
}

Future.get() gives 3 exceptions as output:

  1. InterruptedException — In case the task is interrupted
  2. ExecutionException — In case the task is completed exceptionally where you get the exceptions that occurred in the task.
  3. CancellationException — In case the task is canceled, and you try to call the get(). This extends the RuntimeException (unchecked exception). So it doesn’t complain about catching it.

Future.get() With Waiting Period

Future.get()comes with an overloaded method that takes timeout and TimeUnit:

V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;

Now it throws an additional exception, TimeoutException

Javapublic Integer getResult()
{
Future<Integer> future = submitCallable();
try
{
return future.get(1, TimeUnit.SECONDS);
}
catch (InterruptedException e)
{
log.error("You task has been interrupted", e);
}
catch (ExecutionException e)
{
log.error("You task has been completed exceptionally", e);
}
catch (TimeoutException e)
{
log.error("You task has been by Timeout", e);
}
return Integer.MIN_VALUE;
}

Future.cancel (boolean mayInterruptIfRunning)

As per the docs, this function attempts to cancel the execution of this task. This attempt will fail if the task has already been completed, has already been canceled, or could not be canceled for some other reason. If the token has been created successfully or the task has not started when cancel is called, this task will never run. If the task has already begun, then the mayInterruptIfRunning parameter determines whether the thread executing this task should be interrupted in an attempt to stop the task or not.

Future.isCancel()

Returns true if this task was canceled before it was completed normally.

Handling Task Cancellation Programmatically

There are certain cases where a task cannot be canceled at all. It’s better to handle cancellation programmatically.

Javapackage winkey.articles;
import lombok.extern.slf4j.Slf4j;
/**
* @author Venkatesh Rajendran
*/
@Slf4j
public class TestTask implements Runnable
{
private boolean interrupt = false;
private Service service;
@Override
public void run()
{
service.doStuff(this);
// Thread checks, if task is interrupted
if(isInterrupt())
{
// task interrupted and post processing is skipped
log.error("Interrupted politely");
return;
}
// post processing
}
public void interrupt()
{
interrupt = true;
}
public boolean isInterrupt()
{
return interrupt;
}
}
class Service
{
public void doStuff(TestTask task)
{
try
{
// doing stuff
}
catch (Exception e)
{
// In case of exception, we interrupt the task
task.interrupt();
}
}}

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Ideas2IT

Ideas2IT

We’re a product engineering firm. Our work is cutting-edge, be it in AI-ML, Cloud, DevOps, or IIoT, for an enviable set of clients. Visit www.ideas2it.com.