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
相关产品推荐
相关产品推荐

