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

Servlet/JSP 多執行緒同步陷阱:實例變數為何會成災?

     編輯:Java 程式設計入門

核心誤區:單實例、多執行緒的共享模型

Servlet 容器預設對每個 Servlet 類別 僅實例化一個物件,所有請求執行緒共享此實例。這意味著:

  • 實例變數(成員欄位) 是全域共享狀態 → 非執行緒安全。
  • 區域變數(方法內變數) 分配於執行緒私有堆疊 → 天然執行緒安全。

變數分類與安全性速查表

類別典型代表執行緒安全說明
類別變數application (ServletContext)⚠️ 非安全全應用共享,需同步或用 ConcurrentHashMap
請求範圍request, response, pageContext✅ 安全每請求獨立建立
會話範圍session (HttpSession)⚠️ 非安全同用戶併發請求共享,需同步
配置物件config (ServletConfig)✅ 安全唯讀初始化參數
實例變數Servlet 類別的 private int count;❌ 危險所有執行緒競爭讀寫
區域變數方法內 String param = req.getParameter("x");✅ 安全堆疊封閉,無共享

經典翻車現場

public class CounterServlet extends HttpServlet {
    private int visitCount = 0;  // 實例變數=共享計數器
    
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
            throws IOException {
        visitCount++;  // 非原子操作:讀取→增加→寫回,競態條件必現
        resp.getWriter().println("Visits: " + visitCount);
    }
}

併發測試下,計數器會漏算、亂序,甚至出現負數(極端情況)。

修正方案三選一

方案 A:改用區域變數(首選)

protected void doGet(...) {
    // 從資料庫/Redis/原子計數器讀取,不持有狀態
    long count = atomicCounter.incrementAndGet(); 
    resp.getWriter().println("Visits: " + count);
}

方案 B:ThreadLocal 隔離(慎用)

private ThreadLocal fmtLocal = 
    ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));

適用於「昂貴且非執行緒安全物件」的複用(如 SimpleDateFormat),但需防止記憶體洩漏(Tomcat 執行緒池復用)。

方案 C:同步機制(效能代價大)

private final AtomicInteger counter = new AtomicInteger(0); // 推薦
// 或
synchronized(this) { visitCount++; } // 粗粒度鎖,降低吞吐

JSP 隱含物件同步提醒

  • page、pageContext、request、response:請求級,安全。
  • session、application:共享級,非安全,操作屬性時需同步。
  • config:初始化級,唯讀,安全。

最佳實踐原則

  1. 無狀態設計:Servlet/JSP 不持有可變實例狀態,所有資料從請求、會話、資料庫、快取取得。
  2. 不可變物件:共享配置、工具類盡量不可變(final 欄位、集合不可變視圖)。
  3. 原子類別優於鎖:AtomicLong、ConcurrentHashMap、LongAdder 取代 synchronized。
  4. 請求範圍傳遞:控制器 → 服務層 → 視圖,參數層層透傳,避免塞入實例欄位。

結語

「Servlet 單實例多執行緒」是 Java Web 開發者必須內化的核心心智模型。下次寫 private int x; 時,請自問:「這個變數會被百萬併發請求同時讀寫嗎?」 若答案是肯定,立即重構為區域變數或執行緒安全替代品。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved