Servlet 和同步

发布于 2024-10-24 22:14:24 字数 1907 浏览 2 评论 0原文

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

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

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

我是否应该在 DbUtils 内采取额外的同步操作?

实际上我想将 HttpServlet 继承为 DatabaseInvokerServlet 之类的东西:

公共抽象类 DatabaseInvokerServlet

使用方法扩展了 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 复制粘贴数据库调用块,

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

Servlets runs in several threads, so my question is:

If I have a lot of servlets which call some utility class (DbUtils, for example

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

should I assume additional actions for synchronization inside DbUtils?

Actually I'd like to inherit HttpServlet into something like DatabaseInvokerServlet:

public abstract class DatabaseInvokerServlet extends HttpServlet

with methods:

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");

}

Then I'd just inherit my DatabaseInvokerServlet to new servlets to do custom stuff. The reason is not to copy-paste database invoke block with try-catch-finally in a lot of places.

But as I can see such approach won't work because of synchronization issues. Am I right?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

寂寞花火° 2024-10-31 22:14:24

如果 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.* 导入。

另请参阅:

If the DbUtils creates the connection in the same thread, like as:

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

Then it's threadsafe.

But if the connection is a class variable, like as:

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

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

Then it is definitely not threadsafe because the same connection will be shared among all threads. Also when it's closed in a thread, all subsequent threads won't be able to use the connection because it's not open anymore. Also when it's never closed, the DB will timeout the connection sooner or later, usually after a few hours, and your application won't work anymore because the connection is not open anymore.


As to the servlet,

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

it's definitely not threadsafe. You're assigning the current user, request and response as instance variables. From each servlet class, there is only one instance during the application's lifetime. This instance is shared among all visitors/sessions throughout the entire application's lifetime. Each HTTP request operates in a separate thread and uses the same instance.

Imagine two simultaneous visitors: visitor A will set the current user, request and response. The DB process however takes a long time. Before the response of visitor A has returned, visitor B calls the same servlet and thus the current user, request and response will be overriden. Then, the query of visitor A finishes and wants to write to the response, it is instead writing to the response of visitor B! Visitor B sees the result of the query of visitor A and visitor A sees nothing on his screen!

You should never assign request/session-specific data as instance variable of the servlet. You should keep them method (thread) local.

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!
    }
}

As to the complete picture, this approach is clumsy. The database access layer should have nothing to do with servlets. It should operate in its own standalone classes which you could just construct/invoke in every other Java class, any servlet class, or a normal application with main(), or whatever. You should not have any single line of java.sql.* imports in your servlet classes (expect of maybe SQLException if it is not abstracted away). You should not have any single line of javax.servlet.* imports in your database classes.

See also:

睡美人的小仙女 2024-10-31 22:14:24

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

If the utility class has state (example: class or instance variables) most probably yes.

银河中√捞星星 2024-10-31 22:14:24

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

If I guess right the DBUtils is returning new instance for each call of getConnection(). And as the DBUtils class is a utility class so it shouldn't be maintaining any state. In this scenario no you dont need any addition efforts for synchronization.

能怎样 2024-10-31 22:14:24

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.

Servlets runs in several threads.

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 specs

A servlet container may send
concurrent requests through the
service method of the servlet. To
handle the requests the developer of
the servlet must make adequate
provisions for concurrent processing
with multiple threads in the service
method.

Synchronisation in servlet.

Never have an member variable in a servlet, it is not thread safe.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文