多线程环境中的连接池超时问题

时间:2012-06-01 07:10:54

标签: java multithreading connection-pooling

我的团队必须进行一些更改并续订旧的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类通常使用包含在synchronized方法中的简单JDBC语句与DB交互(某些类是静态的)。 DataSource在服务器中配置。

public ThatDataAccessor {

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

问题是主线程需要连接到数据库,当这些守护程序线程正在运行时,我们可以轻松地从池中的可用连接中运行,获得“等待连接超时”异常。此外,有时甚至那些守护程序线程都会获得相同的异常。

我们必须摆脱这个问题。

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

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

我逐个分析了线程类,只要它们没有并行运行,似乎没有瓶颈来证明那些“等待连接超时”。 该应用程序使用Oracle 11g在WebSphere 7上运行。

1 个答案:

答案 0 :(得分:1)

您可能错过了某个地方的finally块,以便将连接返回到池中。使用hibernate时,我认为这可能是在调用close()或可能用于事务时调用rollback()时完成的。但无论如何我会打电话给你。

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

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块...例如,查看this hibernate教程,并跳到最底层的例子。您将在try {}中看到他使用tx.commit()并在catch {}中使用tx.rollback(),但他没有session.close(),也没有最终。所以即使他在try和catch中添加了一个“session.close()”,如果他的try块抛出了一些不同于RuntimeException的东西,或者他的catch在try之前引起了额外的Exception或者在rollback之前引起了非HibernateException() ,他的联系不会被关闭。没有session.close(),我认为这实际上并不是很好的代码。但即使代码看似有效,最终也会保证您可以免受此类问题的侵害。

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

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