1

我的团队必须进行一些更改并更新旧的 Web 应用程序。该应用程序有一个主线程和 5 到 15 个守护线程,用作工作线程,用于在数据库中检索和插入数据。

所有这些线程都具有这种设计(为方便起见,此处进行了简化):

public MyDaemon implements Runnable {

     // initialization and some other stuffs

     public void run() {
         ...
         while(isEnabled) {
              Engine.doTask1();
              Engine.doTask2();
              ...
              Thread.sleep(someTime);
         }
     }
}

Engine 类提供了一系列静态方法,用于处理 DataAccessor 类的其他方法,其中一些方法是静态的:

public Engine {

    public static doTask1() {
        ThisDataAccessor.retrieve(DataType data);
        // some complicated operations
        ThisDataAccessor.insertOrUpdate(DataType data);
    }

    public static doTask2() {
        ThatDataAccessor da = new ThatDataAccessor();
        da.retrieve(DataType data);
        // etc.
    }
    ...
}

DataAccessor 类通常使用包含在同步方法中的简单 JDBC 语句与 DB 交互(对于某些类是静态的)。DataSource 在服务器中配置。

public ThatDataAccessor {

    public synchronized void retrieve(DataType data) {
         Connection conn = DataSource.getConnection();
         // JDBC stuff
         conn.close();
    }
    ...
}

问题是主线程需要连接到数据库,当这些守护线程工作时,我们很容易用完池中的可用连接,出现“等待连接超时”异常。此外,有时甚至那些守护线程也会得到相同的异常。

我们必须摆脱这个问题。

我们有一个配置了 20 个连接的连接池,不能再添加了,因为“20”是我们的生产环境标准。一些代码块需要同步,即使我们计划只在真正需要的地方移动“同步”关键字。但我不认为这会带来真正的不同。

我们在多线程编程方面没有经验,我们以前从未遇到过这个连接池问题,这就是为什么我要问:问题是由于这些线程的设计造成的吗?有没有我们没有注意到的缺陷?

我已经一一分析了线程类,只要它们没有并行运行,似乎就没有瓶颈可以证明那些“等待连接超时”的合理性。该应用程序使用 Oracle 11g 在 WebSphere 7 上运行。

4

1 回答 1

1

您可能会在某处丢失 finally 块以将连接返回到池中。对于休眠,我认为这可能在您调用 close() 时完成,或者在您调用 rollback() 时可能用于事务。但无论如何我都会打电话给close。

例如,我自己编写了一个快速而肮脏的池来扩展一个旧应用程序以使其成为多线程,这里是一些处理代码(除了 finnally 块之外,这些代码对你来说应该没有意义):

try {
    connection = pool.getInstance();
    connection.beginTransaction();
    processFile(connection, ...);
    connection.endTransaction();
    logger_multiThreaded.info("Done processing file: " + ... );
} catch (IOException e) {
    logger_multiThreaded.severe("Failed to process file: " + ... );
    e.printStackTrace();
} finally {
    if (connection != null) {
        pool.releaseInstance(connection);
    }
}

人们无法正确使用 finally 块是很常见的……例如,查看这个hibernate 教程,然后跳到最底部的示例。你会看到在 try{} 中他使用了 tx.commit(),在 catch{} 中他使用了 tx.rollback(),但是他没有 session.close(),也没有 finally。因此,即使他在 try 和 catch 中添加了“session.close()”,如果他的 try 块抛出了 RuntimeException 以外的东西,或者他的 catch 在 try 之前导致了额外的异常,或者在 rollback() 之前导致了非 HibernateException ,他的连接不会关闭。如果没有 session.close(),我认为这实际上不是很好的代码。但是,即使代码看起来可以正常工作,a finally 也可以确保您免受此类问题的影响。

所以我会重写他的方法,使用 Session 来匹配这个hibernate文档页面上显示的习语。(而且我也不建议他抛出 RuntimeException,但这是一个不同的话题)。

因此,如果您使用的是 Hibernate,我认为以上内容已经足够了。但除此之外,如果您需要特定的代码帮助,则需要更具体,否则您应该使用 finally 来确保连接关闭的简单想法就足够了。

于 2012-06-01T08:20:07.627 回答