Hi. I am getting a ConcurrentModificationException exception in the Spring's InjectionMetadata class (located in the org.springframework.beans.factory.annotation package) when creating an instance of prototype bean in a multithreaded environment.
Here's the stack of the exception (only Spring portion of it):
I think I know what's going on here. The AbstractAutowireCapableBeanFactory.doCreateBean() method (5th from the top of the stack) synchronizes on the bean definition object:Code:Thread [MaintenanceTasksJob] (Suspended (exception ConcurrentModificationException)) LinkedHashMap$KeyIterator(LinkedHashMap$LinkedHashIterator<T>).remove() line: 364 InjectionMetadata.checkConfigMembers(RootBeanDefinition) line: 72 AutowiredAnnotationBeanPostProcessor.postProcessMergedBeanDefinition(RootBeanDefinition, Class, String) line: 216 DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).applyMergedBeanDefinitionPostProcessors(RootBeanDefinition, Class, String) line: 789 DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).doCreateBean(String, RootBeanDefinition, Object[]) line: 487 DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).createBean(String, RootBeanDefinition, Object[]) line: 450 DefaultListableBeanFactory(AbstractBeanFactory).doGetBean(String, Class<T>, Object[], boolean) line: 309 DefaultListableBeanFactory(AbstractBeanFactory).getBean(String) line: 189 ClassPathXmlApplicationContext(AbstractApplicationContext).getBean(String) line: 1044
However, two methods later, in the AutowiredAnnotationBeanPostProcessor.postProcessMe rgedBeanDefinition(), an instance of the InjectionMetadata class for the bean is fetched based on the bean class and not bean definition object, which means that instances of InjectionMetadata are cached per bean class. Therefore despite the fact that two threads cannot enter the same post processing section for the same bean instance, they can enter it for the same bean class and therefore will share the same InjectionMetadata instance. Thus in the next method:Code:synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); mbd.postProcessed = true; } }
The it.remove() will potentially throw the exception.Code:public void checkConfigMembers(RootBeanDefinition beanDefinition) { for (Iterator<InjectedElement> it = this.injectedElements.iterator(); it.hasNext();) { if (!beanDefinition.isExternallyManagedConfigMember(member)) beanDefinition.registerExternallyManagedConfigMember(member); } else { it.remove(); } } }
To summarize the problem: when defining two or more prototype beans based on the same class in a multithreaded environment, a ConcurrentModificationException may be thrown in the InjectionMetadata class.
Am I right in my analysis?
Thanks.


Reply With Quote
