首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Servlet 3.0 異步處理全解析:AsyncContext 核心 API、線程池模式與 Ajax 長連接實戰

     編輯:Java 綜合教學

Servlet 3.0 異步處理全解析

傳統 Servlet 同步阻塞模型下,請求線程需等待業務邏輯(DB 查詢、外部 API 調用、耗時計算)完成才能響應,線程資源嚴重浪費。Servlet 3.0 引入異步機制,允許請求線程迅速釋放,業務邏輯轉至異步線程處理,完成後再寫回響應,大幅提升吞吐量。

核心配置:啟用異步支持

注解方式

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

web.xml 方式

<servlet>
    <servlet-name>asyncServlet</servlet-name>
    <servlet-class>com.example.AsyncServlet</servlet-class>
    <async-supported>true</async-supported>
</servlet>

動態註冊

ServletRegistration.Dynamic dynamic = servletContext.addServlet("async", new AsyncServlet());
dynamic.setAsyncSupported(true);
dynamic.addMapping("/async");

核心組件:AsyncContext

調用 request.startAsync() 獲取 AsyncContext,它保存了原始 ServletRequest 和 ServletResponse 的引用,使異步線程能繼續讀取請求參數、寫入響應數據。

方法說明
getRequest()獲取請求對象,異步環境下同步使用
getResponse()獲取響應對象
start(Runnable)提交異步任務到容器管理的線程池(推薦)
complete()標記異步處理結束,提交響應
dispatch(path)異步轉發到其他 Servlet/JSP
setTimeout(ms)設置異步處理超時時間,負數表示無超時
addListener(AsyncListener)註冊生命週期監聽器(onStartAsync, onComplete, onTimeout, onError)

兩種主流異步處理模式

模式一:容器線程池托管

@WebServlet(urlPatterns = "/async", asyncSupported = true)
public class AsyncDemoServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        final AsyncContext ac = req.startAsync();
        ac.setTimeout(5000);
        ac.start(() -> {
            try {
                TimeUnit.SECONDS.sleep(3); // 模擬耗時業務
                PrintWriter out = ac.getResponse().getWriter();
                out.write("異步處理完成");
                out.flush();
                ac.complete(); // 必須調用
            } catch (Exception e) { e.printStackTrace(); }
        });
        // 主線程可立即返回或做其他事
        resp.getWriter().write("請求已接收,異步處理中...");
        resp.flushBuffer();
    }
}

模式二:自定義線程池 + 隊列(生產級推薦)

@WebServlet(urlPatterns = "/async-queue", asyncSupported = true)
public class AsyncQueueServlet extends HttpServlet {
    private final BlockingQueue<AsyncContext> queue = new LinkedBlockingQueue<>(100);
    private final ExecutorService workerPool = Executors.newFixedThreadPool(10);

    @Override
    public void init() {
        for (int i = 0; i < 5; i++) workerPool.submit(this::processQueue);
    }

    private void processQueue() {
        while (true) {
            try {
                AsyncContext ac = queue.take();
                HttpServletRequest req = (HttpServletRequest) ac.getRequest();
                HttpServletResponse resp = (HttpServletResponse) ac.getResponse();
                // 業務邏輯
                TimeUnit.SECONDS.sleep(2);
                PrintWriter out = resp.getWriter();
                out.write("隊列處理完成: " + req.getParameterMap());
                out.flush();
                ac.complete();
            } catch (Exception e) { e.printStackTrace(); }
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        try {
            queue.put(req.startAsync()); // 入隊,非阻塞
            resp.getWriter().write("已加入異步隊列處理");
        } catch (InterruptedException e) { e.printStackTrace(); }
    }
}

Ajax 長輪詢/Comet 實現思路

首次請求保存 AsyncContext 到應用級 Map(key 為用戶 ID),後續 Ajax 請求從 Map 取出 AsyncContext 直接寫入響應,實現服務端主動推送。需注意併發安全與超時清理。

關鍵注意點

  • complete() 必須調用,否則連接懸掛導致泄漏;若異步轉發目標不支持異步,容器會自動調用。
  • 異步線程中使用 ServletRequest/ServletResponse 需確保線程安全,避免與主線程競爭。
  • 監聽器 onTimeout 中應調用 ac.complete() 釋放資源。
  • 同一請求不可重複調用 startAsync(),會拋出 IllegalStateException。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved