Ответ 1
Вы смешиваете разные понятия. Вы должны различать:
- Асинхронная обработка запросов в соответствии с Servlet 3.0; API, который позволяет отделить входящий запрос сервлета от пула потоков веб-контейнера. Он не создает потоки на лету. Он предоставляет пользователю интерфейс для реализации правильного многопоточного решения. Это не относится к неблокирующему IO.
- Пул потоков; предоставляет механизм для получения и управления потоками в веб-контейнере. Когда дело доходит до асинхронного запроса, у вас есть 2 варианта. Вы можете определить свой собственный
ExecutorService
и использовать его для дальнейшей обработки запроса, или вы можете создать новыйRunnable
и отправить его на полученныйAsyncContext
, вызываяAsyncContext.start()
. В случае Tomcat в последнем подходе используется пул потоков Tomcat, определенный вserver.xml
. - Неблокирующий IO (NIO); Хотя это асинхронно, это совсем другая история. Он относится к неблокирующим операциям ввода-вывода, таким как диск или сетевой ввод-вывод. Если вы хотите включить NIO для обработки запросов HTTP, посмотрите на документацию Tomcat .
В приведенном ниже примере описывается, как он может работать. Он использует только один поток для рабочих заданий. Если вы запускаете его из двух разных браузеров параллельно, вывод выглядит следующим образом (я использую собственный журнал):
DATE THREAD_ID LEVEL MESSAGE
2011-09-03 11:51:22.198 +0200 26 I: >doGet: chrome
2011-09-03 11:51:22.204 +0200 26 I: <doGet: chrome
2011-09-03 11:51:22.204 +0200 28 I: >run: chrome
2011-09-03 11:51:27.908 +0200 29 I: >doGet: firefox
2011-09-03 11:51:27.908 +0200 29 I: <doGet: firefox
2011-09-03 11:51:32.227 +0200 28 I: <run: chrome
2011-09-03 11:51:32.228 +0200 28 I: >run: firefox
2011-09-03 11:51:42.244 +0200 28 I: <run: firefox
Вы видите, что методы doGet
сразу заканчиваются, а рабочий все еще работает. 2 тестовых запроса: http://localhost:8080/pc/TestServlet?name=chrome
и http://localhost:8080/pc/TestServlet?name=firefox
.
Простой пример сервлета
@WebServlet(asyncSupported = true, value = "/TestServlet", loadOnStartup = 1)
public class TestServlet extends HttpServlet {
private static final Logger LOG = Logger.getLogger(TestServlet.class.getName());
private static final long serialVersionUID = 1L;
private static final int NUM_WORKER_THREADS = 1;
private ExecutorService executor = null;
@Override
public void init() throws ServletException {
this.executor = Executors.newFixedThreadPool(NUM_WORKER_THREADS);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
final String name = request.getParameter("name");
LOG.info(">doGet: " + name);
AsyncContext ac = request.startAsync(); // obtain async context
ac.setTimeout(0); // test only, no timeout
/* Create a worker */
Runnable worker = new TestWorker(name, ac);
/* use your own executor service to execute a worker thread (TestWorker) */
this.executorService.execute(worker);
/* OR delegate to the container */
// ac.start(worker);
LOG.info("<doGet: " + name);
}
}
... и TestWorker
public class TestWorker implements Runnable {
private static final Logger LOG = Logger.getLogger(TestWorker.class.getName());
private final String name;
private final AsyncContext context;
private final Date queued;
public TestWorker(String name, AsyncContext context) {
this.name = name;
this.context = context;
this.queued = new Date(System.currentTimeMillis());
}
@Override
public void run() {
LOG.info(">run: " + name);
/* do some work for 10 sec */
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
ServletResponse response = this.context.getResponse();
response.setContentType("text/plain");
try {
PrintWriter out = response.getWriter();
out.println("Name:\t\t" + this.name);
out.println("Queued:\t\t" + this.queued);
out.println("End:\t\t" + new Date(System.currentTimeMillis()));
out.println("Thread:\t\t" + Thread.currentThread().getId());
out.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
this.context.complete();
LOG.info("<run: " + name);
}
}