Spring registerBeanDefinition抛出java.util.ConcurrentModificationException

时间:2014-12-03 19:59:02

标签: spring

我们有以下一段被多次调用的代码。问题是它在100个请求中抛出一次java.util.ConcurrentModificationException

    BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
    boolean beanDefExists = registry.containsBeanDefinition(beanName);
    if (!beanDefExists) {
        BeanDefinitionBuilder beanDefBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
        beanDefBuilder.setScope(BeanDefinition.SCOPE_PROTOTYPE);
        beanDefBuilder.setLazyInit(false);
        registry.registerBeanDefinition(beanName, beanDefBuilder.getBeanDefinition());
    }
13:18:10,728 INFO  [stdout] (http-executor-threads - 33) java.util.ConcurrentModificationException: null
13:18:10,728 INFO  [stdout] (http-executor-threads - 33)        at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372) ~[na:1.6.0_45]
13:18:10,729 INFO  [stdout] (http-executor-threads - 33)        at java.util.AbstractList$Itr.next(AbstractList.java:343) ~[na:1.6.0_45]
13:18:10,729 INFO  [stdout] (http-executor-threads - 33)        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resetBeanDefinition(DefaultListableBeanFactory.java:714) ~[spring-beans-3.2.2.RELEASE.jar:3.2.2.RELEASE]
13:18:10,729 INFO  [stdout] (http-executor-threads - 33)        at org.springframework.beans.factory.support.DefaultListableBeanFactory.registerBeanDefinition(DefaultListableBeanFactory.java:675) ~[spring-beans-3.2.2.RELEASE.jar:3.2.2.RELEASE]

有人可以为这个问题提供解决方案吗?

1 个答案:

答案 0 :(得分:0)

问题是,两个请求同时进入,他们都想注册一个bean。要了解会发生什么,我们必须查看DefaultListableBeanFactory的源代码。在方法registerBeanDefinition(...)中有一个synchronized块,其中两个线程彼此等待完成。但是在块之后,方法resetBeanDefinition(...)被调用。因此,当第一个线程以synchronized块结束并在reset方法内继续时,第二个线程继续执行synchronized块。第一个线程继续循环遍历集合beanDefinitionNames(第714行),同时第二个线程将bean名称添加到列表中(第669行)。所以你得到一个例外。

如果两个线程都想创建相同的bean,那么你显然只想创建一个并重用它,所以你必须将代码放在一个synchronized块中。这样只有一个线程创建bean而另一个线程已经可以使用它。另一方面,如果两个线程都创建了不同的bean,则只需要同步registerBeanDefinition方法。

synchronized(this) {
    BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
    boolean beanDefExists = registry.containsBeanDefinition(beanName);
    if (!beanDefExists) {
        BeanDefinitionBuilder beanDefBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
        beanDefBuilder.setScope(BeanDefinition.SCOPE_PROTOTYPE);
        beanDefBuilder.setLazyInit(false);
        registry.registerBeanDefinition(beanName, beanDefBuilder.getBeanDefinition());
    }
}
编辑:发现异常的真正原因,解决方案仍然是相同的。