我们有以下一段被多次调用的代码。问题是它在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]
有人可以为这个问题提供解决方案吗?
答案 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());
}
}
编辑:发现异常的真正原因,解决方案仍然是相同的。