Servlet 和同步
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
如果 DbUtils 在同一线程中创建连接,如下所示:
那么它是线程安全的。
但如果连接是一个类变量,如:
那么它绝对不是线程安全的,因为同一个连接将在所有线程之间共享。此外,当它在线程中关闭时,所有后续线程将无法使用该连接,因为它不再打开。此外,当它从未关闭时,数据库迟早会使连接超时,通常是几个小时后,并且您的应用程序将不再工作,因为连接不再打开。
至于servlet,
它肯定不是线程安全的。您将当前用户、请求和响应分配为实例变量。每个 servlet 类在应用程序的生命周期内只有一个实例。在整个应用程序的生命周期中,该实例在所有访问者/会话之间共享。每个 HTTP 请求都在单独的线程中运行并使用相同的实例。
想象两个同时访问者:访问者 A 将设置当前用户、请求和响应。然而,数据库过程需要很长时间。在访问者 A 的响应返回之前,访问者 B 调用相同的 servlet,因此当前用户、请求和响应将被覆盖。然后,访问者 A 的查询完成并想要写入响应,而是写入访问者 B 的响应!访问者 B 看到访问者 A 的查询结果,而访问者 A 在屏幕上看不到任何内容!
您永远不应该将特定于请求/会话的数据分配为 servlet 的实例变量。您应该将它们的方法(线程)保留在本地。
就整体而言,这种方法是笨拙的。数据库访问层应该与 servlet 无关。它应该在自己的独立类中运行,您可以在每个其他 Java 类、任何 servlet 类或带有
main()
的普通应用程序或其他类中构造/调用这些类。您的 servlet 类中不应有任何单行java.sql.*
导入(如果没有抽象出来,可能会出现SQLException
)。您的数据库类中不应有任何单行javax.servlet.*
导入。另请参阅:
If the DbUtils creates the connection in the same thread, like as:
Then it's threadsafe.
But if the connection is a class variable, like as:
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,
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.
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 ofjava.sql.*
imports in your servlet classes (expect of maybeSQLException
if it is not abstracted away). You should not have any single line ofjavax.servlet.*
imports in your database classes.See also:
如果实用程序类具有状态(例如:类或实例变量),则很可能是。
If the utility class has state (example: class or instance variables) most probably yes.
如果我猜对了,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.
Servlet 在多个线程中运行。
Servlet 2.3 规范
Servlet 中的同步。
Servlets runs in several threads.
Servlet 2.3 specs
Synchronisation in servlet.