Вы смешиваете разные понятия. Вы должны различать:
- Асинхронная обработка запросов согласно сервлету 3.0 ; API, который позволяет отделить входящий запрос сервлета от пула потоков веб-контейнера. Он не создает никаких потоков на лету. Пользователь интерфейса реализует правильное многопоточное решение. Это не относится к неблокирующему IO.
- Пул потоков ; предоставляет механизм для получения и управления потоками в веб-контейнере. Когда дело доходит до асинхронной обработки запроса , у вас есть 2 варианта. Вы можете определить свой собственный
ExecutorService
и использовать его для дальнейшей обработки запроса, или вы можете создать новый Runnable
и отправить его полученному AsyncContext
, позвонив по номеру AsyncContext.start()
* 1019. *. В случае 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);
}
}