Asynchronous Programming - AWS SDK for Java 1.x

The AWS SDK for Java 1.x has entered maintenance mode as of July 31, 2024, and will reach end-of-support on December 31, 2025. We recommend that you migrate to the AWS SDK for Java 2.x to continue receiving new features, availability improvements, and security updates.

Asynchronous Programming

You can use either synchronous or asynchronous methods to call operations on AWS services. Synchronous methods block your thread’s execution until the client receives a response from the service. Asynchronous methods return immediately, giving control back to the calling thread without waiting for a response.

Because an asynchronous method returns before a response is available, you need a way to get the response when it’s ready. The AWS SDK for Java provides two ways: Future objects and callback methods.

Java Futures

Asynchronous methods in the AWS SDK for Java return a Future object that contains the results of the asynchronous operation in the future.

Call the Future isDone() method to see if the service has provided a response object yet. When the response is ready, you can get the response object by calling the Future get() method. You can use this mechanism to periodically poll for the asynchronous operation’s results while your application continues to work on other things.

Here is an example of an asynchronous operation that calls a Lambda function, receiving a Future that can hold an InvokeResult object. The InvokeResult object is retrieved only after isDone() is true.

import com.amazonaws.services.lambda.AWSLambdaAsyncClient; import com.amazonaws.services.lambda.model.InvokeRequest; import com.amazonaws.services.lambda.model.InvokeResult; import java.nio.ByteBuffer; import java.util.concurrent.Future; import java.util.concurrent.ExecutionException; public class InvokeLambdaFunctionAsync { public static void main(String[] args) { String function_name = "HelloFunction"; String function_input = "{\"who\":\"SDK for Java\"}"; AWSLambdaAsync lambda = AWSLambdaAsyncClientBuilder.defaultClient(); InvokeRequest req = new InvokeRequest() .withFunctionName(function_name) .withPayload(ByteBuffer.wrap(function_input.getBytes())); Future<InvokeResult> future_res = lambda.invokeAsync(req); System.out.print("Waiting for future"); while (future_res.isDone() == false) { System.out.print("."); try { Thread.sleep(1000); } catch (InterruptedException e) { System.err.println("\nThread.sleep() was interrupted!"); System.exit(1); } } try { InvokeResult res = future_res.get(); if (res.getStatusCode() == 200) { System.out.println("\nLambda function returned:"); ByteBuffer response_payload = res.getPayload(); System.out.println(new String(response_payload.array())); } else { System.out.format("Received a non-OK response from {AWS}: %d\n", res.getStatusCode()); } } catch (InterruptedException | ExecutionException e) { System.err.println(e.getMessage()); System.exit(1); } System.exit(0); } }

Asynchronous Callbacks

In addition to using the Java Future object to monitor the status of asynchronous requests, the SDK also enables you to implement a class that uses the AsyncHandler interface. AsyncHandler provides two methods that are called depending on how the request completed: onSuccess and onError.

The major advantage of the callback interface approach is that it frees you from having to poll the Future object to find out when the request has completed. Instead, your code can immediately start its next activity, and rely on the SDK to call your handler at the right time.

import com.amazonaws.services.lambda.AWSLambdaAsync; import com.amazonaws.services.lambda.AWSLambdaAsyncClientBuilder; import com.amazonaws.services.lambda.model.InvokeRequest; import com.amazonaws.services.lambda.model.InvokeResult; import com.amazonaws.handlers.AsyncHandler; import java.nio.ByteBuffer; import java.util.concurrent.Future; public class InvokeLambdaFunctionCallback { private class AsyncLambdaHandler implements AsyncHandler<InvokeRequest, InvokeResult> { public void onSuccess(InvokeRequest req, InvokeResult res) { System.out.println("\nLambda function returned:"); ByteBuffer response_payload = res.getPayload(); System.out.println(new String(response_payload.array())); System.exit(0); } public void onError(Exception e) { System.out.println(e.getMessage()); System.exit(1); } } public static void main(String[] args) { String function_name = "HelloFunction"; String function_input = "{\"who\":\"SDK for Java\"}"; AWSLambdaAsync lambda = AWSLambdaAsyncClientBuilder.defaultClient(); InvokeRequest req = new InvokeRequest() .withFunctionName(function_name) .withPayload(ByteBuffer.wrap(function_input.getBytes())); Future<InvokeResult> future_res = lambda.invokeAsync(req, new AsyncLambdaHandler()); System.out.print("Waiting for async callback"); while (!future_res.isDone() && !future_res.isCancelled()) { // perform some other tasks... try { Thread.sleep(1000); } catch (InterruptedException e) { System.err.println("Thread.sleep() was interrupted!"); System.exit(0); } System.out.print("."); } } }

Best Practices

Callback Execution

Your implementation of AsyncHandler is executed inside the thread pool owned by the asynchronous client. Short, quickly executed code is most appropriate inside your AsyncHandler implementation. Long-running or blocking code inside your handler methods can cause contention for the thread pool used by the asynchronous client, and can prevent the client from executing requests. If you have a long-running task that needs to begin from a callback, have the callback run its task in a new thread or in a thread pool managed by your application.

Thread Pool Configuration

The asynchronous clients in the AWS SDK for Java provide a default thread pool that should work for most applications. You can implement a custom ExecutorService and pass it to AWS SDK for Java asynchronous clients for more control over how the thread pools are managed.

For example, you could provide an ExecutorService implementation that uses a custom ThreadFactory to control how threads in the pool are named, or to log additional information about thread usage.

Asynchronous Access

The TransferManager class in the SDK offers asynchronous support for working with Amazon S3. TransferManager manages asynchronous uploads and downloads, provides detailed progress reporting on transfers, and supports callbacks into different events.