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

Java 計數器教學(一):修正版——解決併發安全問題

     編輯:Java 相關知識

本系列從零構建網頁訪問計數器,逐步揭示併發安全、持久化、架構分層等核心課題。首篇展示修正版,使用 synchronized 鎖解決 application 域物件的多線程競態條件。

問題背景

計數器邏輯:從 application 取出計數、+1、存回。多用戶並發訪問時,若無同步,會出現「讀取到相同舊值 → 各自+1 → 寫回」導致計數遺漏。

修正版完整代碼

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title>計數器修正版</title></head><body>
<%! 
    // 類級別鎖,全 Web 應用共享
    private static final Object lock = new Object(); 
%>
Hello World!
<% 
    Integer count = 0;
    synchronized (lock) {  // 關鍵:同步塊保護「讀-改-寫」原子性
        // 從 application 域取得計數器
        count = (Integer) application.getAttribute("INDEX_COUNTER");
        // 首次訪問初始化
        if (count == null) {
            count = 0;
            application.setAttribute("INDEX_COUNTER", count);
        }
        count++;
        application.setAttribute("INDEX_COUNTER", count);
    }
%>
當前總訪問次數:<%= count %>
</body></html>

關鍵點解析

  1. 為何用 Integer 而非 int:application.getAttribute 返回 Object,首次訪問返回 null,若強轉 int 會拋 NullPointerException;用 Integer 可判空再自動拆箱。
  2. 鎖物件宣告在 jspInit 隱含物件(<%! %>):確保整個 Web 應用只有一把鎖,所有請求線程競爭同一把鎖。
  3. 同步粒度:僅鎖住「讀取 → 判空初始化 → +1 → 寫回」關鍵路徑,避免過度鎖定影響併發效能。
  4. 模擬併發驗證:在 count = ... 後加 Thread.sleep(10000),雙瀏覽器同時刷新,無鎖版計數只增 1,有鎖版正確增 2。

遺留問題

  • 重啟歸零:application 隨 ServletContext 銷毀而消失,Tomcat 重啟計數歸零。
  • 單點故障:集群環境下各節點各自計數,無法匯總。

下一篇將創建標準 Dynamic Web Project,並改用檔案持久化解決重啟歸零問題。

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