You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Servlet多请求处理异常:返回错误数据给客户端的解决方案咨询

问题根源分析

你遇到的问题核心原因非常明确:QueryFunction类中的connect、statement、rset都是静态成员变量。静态变量属于类本身,而非类的实例,在多线程(也就是多并发请求)场景下,所有请求会共享这些静态变量。当多个请求同时调用getPageDetails方法时,后一个请求会覆盖前一个请求的数据库连接、语句和结果集,最终导致返回给客户端的数据串了。

虽然你给getPageDetails加了synchronized关键字,但这只能保证同一时间只有一个线程执行这个方法,并没有解决静态变量本身的共享问题——而且加了synchronized还会导致所有请求串行执行,严重降低系统的并发性能,这可不是一个好的解决方案。

修复代码的具体步骤

只需要把QueryFunction中的静态成员变量改成方法内部的局部变量,让每个请求线程拥有自己独立的连接、语句和结果集即可:

//QueryFunction Class
public class QueryFunction{
    // 移除所有静态成员变量
    public static List<DrawForm> getPageDetails(String obj){
        List<DrawForm> dataModel = new ArrayList<>();
        // 把连接、语句、结果集定义为方法内部的局部变量
        Connection connect = null;
        CallableStatement statement = null;
        ResultSet rset = null;
        try {
            connect = DbConnection.GetConnection();
            statement = (CallableStatement)connect.prepareCall("{call Get_form_template(?)}");
            statement.setString(1, obj);
            rset = statement.executeQuery();
            while(rset.next()){
                DrawForm form = new DrawForm();
                // 这里填充DrawForm的属性,比如:
                // form.setId(rset.getInt("id"));
                // form.setName(rset.getString("name"));
                dataModel.add(form);
            }
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            // 关闭资源的方法也需要调整,接收局部变量作为参数
            close(connect, rset, statement);
        }
        return dataModel;
    }
    // 修改close方法,接收局部的资源变量
    private static void close(Connection connect, ResultSet rset, CallableStatement statement) {
        if (rset != null) {
            try {
                rset.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (statement != null) {
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (connect != null) {
            try {
                connect.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        // 注意:关闭资源的顺序要正确,先关ResultSet,再关Statement,最后关Connection
    }
}
多请求场景下处理请求与响应的最佳实践
  • 绝对不要用静态变量存储请求相关状态:每个HTTP请求对应一个独立线程,所有和当前请求相关的数据(比如数据库连接、请求参数、处理结果等)都应该放在线程局部的变量中(比如方法局部变量,或者ThreadLocal),避免多个请求互相干扰。
  • 使用数据库连接池替代直接创建连接:每次请求都新建连接效率极低,使用连接池(比如Apache Commons DBCP、HikariCP)可以复用连接,提升性能和稳定性。连接池会自动管理连接的创建、复用和销毁,你只需要从池中获取连接,用完后归还即可。
  • 保证Servlet的线程安全性:Servlet默认是单例的,所以绝对不要在Servlet类中定义成员变量存储请求相关数据,所有请求处理的变量都应该放在doGet/doPost方法内部。
  • 避免不必要的同步:synchronized会导致并发请求串行执行,严重影响性能。只有当你真的需要共享可变状态时才使用同步,像你的场景,只要每个线程有自己的独立资源,完全不需要同步。
关于单例和代理类的说明

你提到的单例模式和代理类,其实和当前问题关系不大:

  • 单例模式是保证一个类只有一个实例,通常用于全局共享的无状态服务(比如配置类),但绝对不能用来存储请求相关的状态。
  • 代理类一般用于增强原有类的功能(比如日志、事务),并不能直接解决你现在的线程安全问题。

内容的提问来源于stack exchange,提问作者Sunil Subramannian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:02:28