The AWS SDK for Java 1.x has entered maintenance mode as of July 31, 2024,
and will reach end-of-support
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
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
For example, you could provide an ExecutorService
implementation that uses a custom
ThreadFactory
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.