java - 与 DBCP 连接池和线程的混淆

标签 java multithreading spring hibernate apache-commons-dbcp

我正在尝试让多线程在我的 Java Web 应用程序中工作,似乎无论我尝试什么,我都会遇到一些连接池问题。

我当前的流程是循环遍历所有部门并对其进行处理,最终生成显示。这需要时间,所以我想为每个部门生成一个线程并让它们同时处理。

经过大量时间首先弄清楚如何让我的 hibernate session 在线程中保持打开状态以防止延迟初始化加载错误,我终于找到了为我的 Thread 类创建一个 Spring bean 的解决方案,并创建一个新的每个线程的该 bean 的实例。我尝试了2个不同的版本

1) 我直接将 DAO 类注入(inject)到 Bean 中。失败 - 加载页面几次后,每个线程都会收到“无法获取连接,池错误等待空闲对象超时”,并且应用程序将崩溃。

2) 好的,然后我尝试将 spring SessionFactory 注入(inject)到我的 bean 中,然后创建 DAO 的新实例并使用 SessionFactory 设置它。我的 DAO 对象都扩展了 HibernateDaoSupport。失败 - 加载页面几次后,我会收到“连接数太多”错误消息。

现在我感到困惑的是,我的 SessionFactory bean 是一个单例,我理解这意味着它是在整个 Spring 容器中共享的单个对象。如果这是真的,那么为什么每个线程看起来都在创建一个新连接,而它应该只是共享该单个实例呢?看来我的所有连接池都被填满了,我不明白为什么。一旦线程完成,所有创建的连接都应该被释放,但没有。我什至尝试在注入(inject)的 SessionFactory 上运行 close() 操作,但这没有效果。我尝试将并发运行的线程数限制为 5,希望这不会导致一次创建那么多连接,但没有成功。

我显然做错了什么,但我不确定是什么。我是否完全采用了错误的方法来尝试将我的 hibernate Session 放入我的 Thread 中?我是否没有正确管理我的连接池?任何想法将不胜感激!

我想到的更多信息:我的进程总共创建了大约 25 个线程,这些线程一次运行 5 个。在开始出现错误之前,我可以刷新页面大约 3 次。因此显然每次刷新都会创建并保留一堆连接。

这是我的 spring 配置文件:

<bean id="mainDataSource" class="org.apache.commons.dbcp.BasicDataSource"
    destroy-method="close">

....

<!-- Purposely put big values in here trying to figure this issue out 
since my standard smaller values didn't help.  These big values Fail just the same -->

    <property name="maxActive"><value>500</value></property>
    <property name="maxIdle"><value>500</value></property>
    <property name="minIdle"><value>500</value></property>
    <property name="maxWait"><value>5000</value></property>
    <property name="removeAbandoned"><value>true</value></property>
    <property name="validationQuery"><value>select 0</value></property>
</bean>

<!--Failed attempt #1 -->
<bean id="threadBean" class="controller.manager.ManageApprovedFunds$TestThread" scope="prototype">
        <property name="dao"><ref bean="dao" /></property>
    </bean>

<!--Failed attempt #2 -->
<bean id="threadBean" class="manager.ManageApprovedFunds$TestThread" scope="prototype">
        <property name="sessionFactory"><ref bean="sessionFactory" /></property>
    </bean>

Java代码:

ExecutorService taskExecutor = Executors.newFixedThreadPool(5);

for(Department dept : getDepartments()) {
    TestThread t = (TestThread)springContext.getBean("threadBean");
    t.init(dept, form, getSelectedYear());
    taskExecutor.submit(t);
}

taskExecutor.shutdown();

public static class TestThread extends HibernateDaoSupport implements Runnable, ApplicationContextAware {
        private ApplicationContext appContext;

        @Override
        public void setApplicationContext(ApplicationContext arg0)
                throws BeansException {
            this.appContext = arg0;
        }

        @Override
        public void run() {
            try {
                MyDAO dao = new MyDAO();
                dao.setSessionFactory(getSessionFactory());

                //SOME READ OPERATIONS

                getSessionFactory().close();
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }       
    }

最佳答案

基本上你不应该尝试共享单个 hibernate Session线程之间。 Hibernate session 和实体对象都是线程安全的,这可能会导致一些令人惊讶的问题。 Here [这里]3[] 是一篇小而精彩的读物,评论中也有一些值(value)。基本上不要尝试分享 Session线程之间。

还有另一个问题,因为整个事务管理是基于ThreadLocal对象,获取当前 session 和底层 JDBC 连接也是如此。现在尝试生成线程将导致令人惊讶的问题,其中之一就是连接池饥饿。 (注意:不要打开太多连接,更多信息here)。

除了不要打开太多连接之外,您还应该注意启动许多线程。线程绑定(bind)到 cpu(如果有多个核心,则绑定(bind)到核心)。添加多个线程可能会导致多个线程之间大量共享单个 cpu/核心。这不仅不会提高你的表现,反而会降低你的表现。

简而言之,恕我直言,您的方法是错误的,每个线程应该简单地读取它关心的实体,做它的事情并提交事务。我建议使用类似 Spring Batch 的东西为此,而不是发明自己的机制。 (尽管如果它足够简单,我可能会选择它)。

关于java - 与 DBCP 连接池和线程的混淆,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25172331/

相关文章:

java - @Scheduled 和 @Transactional 方法中的 Spring-data-jpa 延迟加载

c# - 生产者消费者在 C# 中使用 AutoReset 事件

java - 用于 ElasticSearch 的 Spring Boot Micrometer 与 APM Java 代理

java - Spring配置文件错误

java - Spring MVC JSP JSTL 退出每个带有 boolean 值的循环

java - 如果时区的日期时间不明确,是否抛出异常?

java - 无法从标准 JAR 文件中读取 TLD "META-INF/c.tld"

java - zipalign 未随 Java SDK/JRE 一起安装

python - 从多进程切换到多线程 Dask.DataFrame

android - 应用发明家 : Asynchonous tasks?