【问题标题】:Thread safety in Java web application data access classJava Web 应用程序数据访问类中的线程安全
【发布时间】:2009-02-21 15:11:24
【问题描述】:

我的一个爱好项目是 Java Web 应用程序。这是一个带有表单的简单网页。用户填写表单,提交,然后显示一些结果。

数据来自 JDBC 连接。当用户提交时,我验证输入,构建“CREATE ALIAS”语句、“SELECT”语句和“DROP ALIAS”语句。我执行它们并对查询中的 ResultSet 做任何我需要做的事情。

由于我正在使用的特定数据库/JDBC 组合上的别名存在问题,因此每次运行查询时都需要使用唯一名称创建这些别名。我使用 int 来确保每次访问数据库时都会递增。

所以,我的数据访问类看起来有点像:

private final static Connection connection = // initialized however

private static int uniqueInvocationNumber = 0;

public static Whatever getData(ValidatedQuery validatedQuery) {
    String aliasName = "TEMPALIAS" + String.valueOf(uniqueInvocationNumber);
    // build statements, execute statements, deal with results
    uniqueInvocationNumber++;
}

这行得通。然而,我最近意识到我被 Jon Skeet 的threading knowledge 的第 0 阶段牢牢困住(“完全无知 - 忽略任何可能的问题。”)——我从未编写过线程代码或线程感知代码。我完全不知道当许多用户同时使用该应用程序时会发生什么。

所以我的问题是,(假设我没有靠运气/J2EE 魔法偶然发现线程安全):

我怎样才能保证这个安全?

我在此处提供了我认为相关的信息,但如果还不够,请告诉我。

谢谢一百万。

编辑: 这是一个使用 Wicket 框架的合适的 J2EE Web 应用程序。我通常将它部署在 Jetty 中。

编辑:关于 ALIAS 动机的长篇故事,供感兴趣的人参考:

有问题的数据库是 AS400 上的 DB2(i5、System i、iSeries,无论 IBM 现在如何称呼它),我使用的是 jt400

虽然 AS400 上的 DB2 有点像任何其他平台上的 DB2,但由于遗留的东西,表有一个“成员”的概念。成员有点像一张桌子。我要运行的查询是

SELECT thisField FROM thisTable(thisMember)

它将 thisMember 本身视为一个表,因此只需为您提供成员中所有行的 thisField。

现在,诸如此类的查询在交互式 SQL 会话中运行良好,但不能通过 JDBC 运行(我不知道为什么)。我使用的解决方法是做类似的事情

CREATE ALIAS tempAlias FOR thisTable(thisMember)

然后一个

SELECT thisField FROM tempAlias

然后一个

DROP ALIAS tempAlias

这很有效,但是对于一个显示停止的问题:当您使用始终称为“tempAlias”的 ALIAS 重复执行此操作时,如果 thisField 从一个查询到下一个查询的长度不同,结果集就会出现乱码对于第二个查询(第一行的 getString 很好,下一个前面有一定数量的空格,下一个前面有相同数量的空格 - 这是来自内存,但它是这样的)。

因此,确保每个 ALIAS 都有一个不同的名称的解决方法可以解决这个问题。

我刚刚意识到(已经花时间挖掘了这个解释)我可能没有花足够的时间在考虑解决方法之前首先考虑这个问题。不幸的是,我还没有实现为卧室安装 AS400 的梦想;)所以我现在无法尝试任何新的东西。

【问题讨论】:

  • 这是一个向用户提供 Web 界面的独立应用程序,还是在应用程序服务器(Tomcat、JBoss、WebSphere 等)中运行?

标签: java multithreading jakarta-ee


【解决方案1】:

好吧,我暂时忽略任何 SQL 内容,只关注 uniqueInvocationNumber 部分。这里有两个问题:

  • 无法保证线程会在任何特定点看到最新值
  • 增量不是原子的

在 Java 中解决此问题的最简单方法是使用 AtomicInteger

private static final AtomicInteger uniqueInvocationNumber = new AtomicInteger();

public static Whatever getData(ValidatedQuery validatedQuery) {
    String aliasName = "TEMPALIAS" + uniqueInvocationNumber.getAndIncrement()
    // build statements, execute statements, deal with results
}

请注意,这仍然假设您只在单个服务器上运行单个实例。对于一个家庭项目,这可能是一个合理的假设:)

另一个潜在问题是在不同线程之间共享单个连接。通常,处理数据库连接的更好方法是使用连接池,并在需要的地方“打开/使用/关闭”连接(在 finally 块中关闭连接)。

【讨论】:

    【解决方案2】:

    如果该静态变量和唯一调用编号的递增对所有请求都是可见的,我会说它是需要同步的共享状态。

    【讨论】:

      【解决方案3】:

      我知道这不能回答您的问题,但我会认真考虑重新实现该功能,因此不需要创建所有这些别名。 (您能解释一下您正在创建什么样的别名以及为什么需要它?)

      我知道这只是一个业余项目,但请考虑将您的“待办事项列表”添加到使用连接池。这都是学习的一部分,我想这也是你做这个项目的动力的一部分。连接池是处理数据库支持的网络应用程序中的多个同时用户的正确方法。

      【讨论】:

      • 嗨。是的,我将实施连接池的想法,当然在我让其他人使用它之前。如果您有兴趣,我将添加有关上述 ALIAS 的编辑。
      猜你喜欢
      • 2014-09-19
      • 2011-02-21
      • 1970-01-01
      • 2015-07-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-26
      • 2011-07-18
      相关资源
      最近更新 更多