Ответ 1
В традиционной модели Servlet обычно бывает, что 1 запрос соответствует 1 потоку.
Эти потоки обычно поступают из пула, который управляется контейнером Servlet. Контейнер Servlet может обрабатывать только новые запросы, если в этом пуле есть свободные потоки. Пока ваш собственный код занят обработкой запроса, поток не является бесплатным.
В некоторых ситуациях, возможно, стоит сломать эту модель. Случается, что запрос поступает в Servlet через такой поток управления контейнером Servlet, и тогда ваш код запрашивает асинхронное выполнение. Затем вы можете вернуться из запроса Servlet, и поток контейнера будет освобожден.
В отличие от синхронной обработки запросов, это не приведет к ответу и не приведет к закрытию соединения. Вместо этого вы можете передать асинхронный контекст другому пулу потоков, который может его поднять, и когда какой-то поток будет свободно обрабатывать его, обслуживайте его и сможете записать ответ.
Пример:
@WebServlet(urlPatterns = "/somepath", asyncSupported = true)
public class AsyncServlet extends HttpServlet {
@EJB
private AsyncBean asyncBean;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
AsyncContext asyncContext = request.startAsync();
// The following line will not block and return immediately
asyncBean.doAsyncStuff(asyncContext);
} // Shortly after this method has ended, thread will be returned to pool
}
С AsyncBean
реализуется как:
@Stateless
public class AsyncBean {
@Asynchronous
public void doAsyncStuff(AsyncContext asyncContext) throws IOException {
asyncContext.getResponse().getWriter().write("test");
}
}
В приведенном выше коде, где-то вскоре после возврата из метода AsyncServlet#doGet()
поток Servlet будет возвращен в пул. "Запрос" (задача) для выполнения AsyncBean#doAsyncStuff()
будет помещен в очередь для сбора пула потоков EJB.
Ответ на ПОЧЕМУ и КОГДА вы будете использовать это не так просто. Если вы просто хотите сохранить потоки, то в приведенном выше случае вы бы обменяли один поток из одного пула потоков для другого (в данном случае пул сервлетов против пула асинхронных EJB), а чистая выгода не будет такой. Вы могли бы также дать вашему потоку потока сервлета дополнительный поток.
В более сложных сценариях вы можете, однако, выполнять более мелкомасштабное управление запросами; разделите их на несколько задач и попросите пул потоков обслуживать эти задачи. Например. Представьте себе 100 запросов на загрузку 10 МБ файла, обработанных 10 потоками, которые round-robin дают 100 Кбайт времени для каждого запроса.
Еще одно приложение - это запрос, который должен ждать данных из внешней системы и где эта внешняя система может отправлять сообщение, которое может быть передано обратно запрашивающему. То есть вызов базы данных здесь не имеет смысла, поскольку в любом случае вам понадобится другой поток, ожидающий ответа. Затем вы снова измените один поток на другой. Но если вам нужно дождаться сообщения о входящем письме, то один поток может дождаться любой электронной почты и реле для любого приостановленного запроса.