AWS SDK for Java
Developer Guide

Asynchronous Programming

AWS SDK for Java 2.0 features truly non-blocking asychronous clients that implements high concurrency across a few threads. AWS SDK for Java 1.11.x has asynchronous clients that are wrappers around a thread pool and blocking synchronous clients which do not provide the full benefit of non-blocking I/O.

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 2.0 asynchronous client methods return CompletableFuture objects that allows you to access the response when it's ready.

Non-streaming Operations

For non-streaming operations, asychronous method calls are similar to synchronous methods except that the asynchronous methods in the AWS SDK for Java return a CompletableFuture object that contains the results of the asynchronous operation in the future.

Call the CompletableFuture whenComplete() method with an action to complete when the result is available. CompletableFuture implements the Future interface so you can get the response object by calling the get() method as well.

Here is an example of an asynchronous operation that calls a DynamoDB function to get a list of tables, receiving a CompletableFuture that can hold a ListTablesResponse object. The action defined in the call to whenComplete() is only done when the asynchronous call is complete.

public class DynamoDBAsync { public static void main(String[] args) throws InterruptedException { // Creates a default async client with credentials and regions loaded from the environment DynamoDbAsyncClient client = DynamoDbAsyncClient.create(); CompletableFuture<ListTablesResponse> response = client.listTables(ListTablesRequest.builder() .build()); // Map the response to another CompletableFuture containing just the table names CompletableFuture<List<String>> tableNames = response.thenApply(ListTablesResponse::tableNames); // When future is complete (either successfully or in error) handle the response tableNames.whenComplete((tables, err) -> { try { if (tables != null) { tables.forEach(System.out::println); } else { // Handle error err.printStackTrace(); } } finally { // Lets the application shut down. Only close the client when you are completely done with it. client.close(); } }); tableNames.join(); } }

Streaming Operations

For streaming operations, you must provide an AsyncRequestBody to provide the content incrementally or an AsyncResponseTransformer to receive and process the response.

Here is an example that uploads a file to Amazon S3 asynchronously with the PutObject operation.

public class S3AsyncOps { private static final String BUCKET = "sample-bucket"; private static final String KEY = "testfile.in"; public static void main(String[] args) { S3AsyncClient client = S3AsyncClient.create(); CompletableFuture<PutObjectResponse> future = client.putObject( PutObjectRequest.builder() .bucket(BUCKET) .key(KEY) .build(), AsyncRequestBody.fromFile(Paths.get("myfile.in")) ); future.whenComplete((resp, err) -> { try { if (resp != null) { System.out.println("my response: " + resp); } else { // Handle error err.printStackTrace(); } } finally { // Lets the application shut down. Only close the client when you are completely done with it. client.close(); } }); future.join(); } }

Here is an example that gets a file from Amazon S3 asynchronously with the GetObject operation.

public class S3AsyncStreamOps { private static final String BUCKET = "sample-bucket"; private static final String KEY = "testfile.out"; public static void main(String[] args) { S3AsyncClient client = S3AsyncClient.create(); final CompletableFuture<GetObjectResponse> futureGet = client.getObject( GetObjectRequest.builder() .bucket(BUCKET) .key(KEY) .build(), AsyncResponseTransformer.toFile(Paths.get("myfile.out"))); futureGet.whenComplete((resp, err) -> { try { if (resp != null) { System.out.println(resp); } else { // Handle error err.printStackTrace(); } } finally { // Lets the application shut down. Only close the client when you are completely done with it client.close(); } }); futureGet.join(); } }