【问题标题】:Servlets and synchronizationServlet 和同步
【发布时间】:2011-07-17 23:11:02
【问题描述】:

Servlets 在多个线程中运行,所以我的问题是:

如果我有很多调用某个实用程序类(例如 DbUtils)的 servlet

Connection c = DbUtils.getConnection();
//....some action with db here

我应该为 DbUtils 中的同步假设额外的操作吗?

其实我想把 HttpServlet 继承成 DatabaseInvokerServlet 之类的东西:

public abstract class DatabaseInvokerServlet extends HttpServlet

方法:

public abstract void getResultSets(Connection connection) throws SQLException;
private AbstractUser currentUser;
private HttpServletRequest request;
private HttpServletResponse response;
protected void processData() {}
protected void afterRequestProcessed() throws ServletException, IOException {}
protected void beforeRequestProcessed() throws ServletException, IOException {}

protected void execute() {
    Connection c = null;
    try {
        c = DbUtils.getConnection();
        getResultSets(c);
        processData();
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        try {
            if (c != null) {
                c.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

public HttpServletRequest getRequest() {
    return request;
}

public HttpServletResponse getResponse() {
    return response;
}

public AbstractUser getCurrentUser() {
    return currentUser;
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    request.setCharacterEncoding("UTF-8");
    response.setContentType("text/html;charset=UTF-8");
    response.setCharacterEncoding("UTF-8");


    this.request = request;
    this.response = response;
    this.currentUser = (AbstractUser) request.getSession().getAttribute("currentUser");

}

然后我只需将我的 DatabaseInvokerServlet 继承到新的 servlet 以执行自定义操作。原因是很多地方都没有用try-catch-finally复制粘贴数据库调用块。

但正如我所见,由于同步问题,这种方法不起作用。我说的对吗?

【问题讨论】:

  • 请贴出DbUtils.getConnection()的代码;

标签: java servlets jdbc synchronization


【解决方案1】:

如果 DbUtils 在同一个线程中创建连接,如:

public static Connection getConnection() throws SQLException {
    return DriverManager.getConnection(url, username, password);
}

那么它是线程安全的。

但是如果连接是一个类变量,比如:

private static Connection connection = DriverManager.getConnection(url, username, password);

public static Connection getConnection() throws SQLException {
    return connection;
}

那么它肯定不是线程安全的,因为相同的连接将在所有线程之间共享。此外,当它在线程中关闭时,所有后续线程都将无法使用该连接,因为它不再打开。此外,当它从未关闭时,数据库迟早会超时连接,通常在几个小时后,并且您的应用程序将不再工作,因为连接不再打开。


关于servlet,

public abstract class DatabaseInvokerServlet extends HttpServlet {
    private AbstractUser currentUser;
    private HttpServletRequest request;
    private HttpServletResponse response;
    // ...
}

绝对不是线程安全的。您将当前用户、请求和响应分配为实例变量。对于每个 servlet 类,在应用程序的生命周期中只有一个实例。此实例在整个应用程序的整个生命周期中在所有访问者/会话之间共享。每个 HTTP 请求都在单独的线程中运行并使用相同的实例。

想象两个同时访问者:访问者 A 将设置当前用户、请求和响应。然而,数据库过程需要很长时间。在访问者 A 的响应返回之前,访问者 B 调用相同的 servlet,因此当前用户、请求和响应将被覆盖。然后,访问者 A 的查询完成并想写入响应,而是写入访问者 B 的响应!访问者 B 看到访问者 A 的查询结果,而访问者 A 在他的屏幕上什么也看不到!

您永远不应将特定于请求/会话的数据分配为 servlet 的实例变量。您应该将它们的方法(线程)保留在本地。

public abstract class DatabaseInvokerServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        AbstractUser currentUser = request.getSession().getAttribute("user");
        // Keep the variables in the method block! 
        // Do not assign them as instance variable!
    }
}

就全貌而言,这种方法很笨拙。数据库访问层应该与 servlet 无关。它应该在自己的独立类中运行,您可以在每个其他 Java 类、任何 servlet 类或带有main() 的普通应用程序或其他任何东西中构造/调用这些类。您的 servlet 类中不应有任何单行 java.sql.* 导入(如果没有抽象出来,则可能是 SQLException)。您的数据库类中不应有任何一行 javax.servlet.* 导入。

另见:

【讨论】:

    【解决方案2】:

    Servlet 在多个线程中运行。

    The J2EE spec says there is only one instance per servlet class running in one web container for non single thread servlet. 
    

    Servlet 2.3 规范

    一个 servlet 容器可以发送 并发请求通过 servlet 的服务方法。到 处理开发者的请求 servlet 必须使 并发处理的规定 服务中有多个线程 方法。

    servlet 中的同步。

    Never have an member variable in a servlet, it is not thread safe.
    

    【讨论】:

      【解决方案3】:

      如果我猜对了,DBUtils 会为每次调用 getConnection() 返回新实例。由于 DBUtils 类是一个实用程序类,因此它不应该保持任何状态。在这种情况下,您不需要任何额外的同步工作。

      【讨论】:

        【解决方案4】:

        如果实用程序类有状态(例如:类或实例变量)很可能是的。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-08-23
          • 1970-01-01
          • 1970-01-01
          • 2011-08-30
          • 2014-05-23
          • 2012-01-31
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多