Asynchronous Processing
Java Platform, Enterprise Edition (Java EE) 8
The Java EE Tutorial

Previous Next Contents

Asynchronous Processing

Web containers in application servers normally use a server thread per client request. Under heavy load conditions, containers need a large amount of threads to serve all the client requests. Scalability limitations include running out of memory or exhausting the pool of container threads. To create scalable web applications, you must ensure that no threads associated with a request are sitting idle, so the container can use them to process new requests.

There are two common scenarios in which a thread associated with a request can be sitting idle.

  • The thread needs to wait for a resource to become available or process data before building the response. For example, an application may need to query a database or access data from a remote web service before generating the response.

  • The thread needs to wait for an event before generating the response. For example, an application may have to wait for a JMS message, new information from another client, or new data available in a queue before generating the response.

These scenarios represent blocking operations that limit the scalability of web applications. Asynchronous processing refers to assigning these blocking operations to a new thread and retuning the thread associated with the request immediately to the container.

Asynchronous Processing in Servlets

Java EE provides asynchronous processing support for servlets and filters. If a servlet or a filter reaches a potentially blocking operation when processing a request, it can assign the operation to an asynchronous execution context and return the thread associated with the request immediately to the container without generating a response. The blocking operation completes in the asynchronous execution context in a different thread, which can generate a response or dispatch the request to another servlet.

To enable asynchronous processing on a servlet, set the parameter asyncSupported to true on the @WebServlet annotation as follows:

@WebServlet(urlPatterns={"/asyncservlet"}, asyncSupported=true)
public class AsyncServlet extends HttpServlet { ... }

The javax.servlet.AsyncContext class provides the functionality that you need to perform asynchronous processing inside service methods. To obtain an instance of AsyncContext, call the startAsync() method on the request object of your service method; for example:

public void doGet(HttpServletRequest req, HttpServletResponse resp) {
   ...
   AsyncContext acontext = req.startAsync();
   ...
}

This call puts the request into asynchronous mode and ensures that the response is not committed after exiting the service method. You have to generate the response in the asynchronous context after the blocking operation completes or dispatch the request to another servlet.

Table 18-3 describes the basic functionality provided by the AsyncContext class.

Table 18-3 Functionality Provided by the AsyncContext Class

Method Signature

Description

void start(Runnable run)

The container provides a different thread in which the blocking operation can be processed.

You provide code for the blocking operation as a class that implements the Runnable interface. You can provide this class as an inner class when calling the start method or use another mechanism to pass the AsyncContext instance to your class.

ServletRequest getRequest()

Returns the request used to initialize this asynchronous context. In the example above, the request is the same as in the service method.

You can use this method inside the asynchronous context to obtain parameters from the request.

ServletResponse getResponse()

Returns the response used to initialize this asynchronous context. In the example above, the response is the same as in the service method.

You can use this method inside the asynchronous context to write to the response with the results of the blocking operation.

void complete()

Completes the asynchronous operation and closes the response associated with this asynchronous context.

You call this method after writing to the response object inside the asynchronous context.

void dispatch(String path)

Dispatches the request and response objects to the given path.

You use this method to have another servlet write to the response after the blocking operation completes.

Waiting for a Resource

This section demonstrates how to use the functionality provided by the AsyncContext class for the following use case:

  1. A servlet receives a parameter from a GET request.

  2. The servlet uses a resource, such as a database or a web service, to retrieve information based on the value of the parameter. The resource can be slow at times, so this may be a blocking operation.

  3. The servlet generates a response using the result from the resource.

The following code shows a basic servlet that does not use asynchronous processing:

@WebServlet(urlPatterns={"/syncservlet"})
public class SyncServlet extends HttpServlet {
   private MyRemoteResource resource;
   @Override
   public void init(ServletConfig config) {
      resource = MyRemoteResource.create("config1=x,config2=y");
   }

   @Override
   public void doGet(HttpServletRequest request,
                     HttpServletResponse response) {
      response.setContentType("text/html;charset=UTF-8");
      String param = request.getParameter("param");
      String result = resource.process(param);
      /* ... print to the response ... */
   }
}

The following code shows the same servlet using asynchronous processing:

@WebServlet(urlPatterns={"/asyncservlet"}, asyncSupported=true)
public class AsyncServlet extends HttpServlet {
   /* ... Same variables and init method as in SyncServlet ... */

   @Override
   public void doGet(HttpServletRequest request,
                     HttpServletResponse response) {
      response.setContentType("text/html;charset=UTF-8");
      final AsyncContext acontext = request.startAsync();
      acontext.start(new Runnable() {
         public void run() {
            String param = acontext.getRequest().getParameter("param");
            String result = resource.process(param);
            HttpServletResponse response = acontext.getResponse();
            /* ... print to the response ... */
            acontext.complete();
   }
}

AsyncServlet adds asyncSupported=true to the @WebServlet annotation. The rest of the differences are inside the service method.

  • request.startAsync() causes the request to be processed asynchronously; the response is not sent to the client at the end of the service method.

  • acontext.start(new Runnable() {…​}) gets a new thread from the container.

  • The code inside the run() method of the inner class executes in the new thread. The inner class has access to the asynchronous context to read parameters from the request and write to the response. Calling the complete() method of the asynchronous context commits the response and sends it to the client.

The service method of AsyncServlet returns immediately, and the request is processed in the asynchronous context.


Previous Next Contents
Oracle Logo  Copyright © 2017, Oracle and/or its affiliates. All rights reserved.