Skip to content

Deadlock while creating a new thread on bean initialization with transactional code invocation [SPR-14388] #18961

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
spring-projects-issues opened this issue Jun 22, 2016 · 1 comment
Assignees
Labels
in: data Issues in data modules (jdbc, orm, oxm, tx) status: backported An issue that has been backported to maintenance branches type: bug A general bug
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented Jun 22, 2016

Bartosz Popiela opened SPR-14388 and commented

"main":
	at org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor.getAdvice(AbstractBeanFactoryPointcutAdvisor.java:84)
	- waiting to lock <0x00000000c17da860> (a java.lang.Object)
	at org.springframework.aop.framework.adapter.DefaultAdvisorAdapterRegistry.getInterceptors(DefaultAdvisorAdapterRegistry.java:79)
	at org.springframework.aop.framework.DefaultAdvisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(DefaultAdvisorChainFactory.java:62)
	at org.springframework.aop.framework.AdvisedSupport.getInterceptorsAndDynamicInterceptionAdvice(AdvisedSupport.java:482)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:190)
	at com.sun.proxy.$Proxy120.transactionalMethod(Unknown Source)
	at com.example.BeanB.transactionalMethod(BeanB.java:219)
	at com.example.BeanB.init(BeanB.java:31)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeCustomInitMethod(AbstractAutowireCapableBeanFactory.java:1638)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1579)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1509)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:521)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458)
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:296)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
	- locked <0x00000000c01b17b0> (a java.util.concurrent.ConcurrentHashMap)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:293)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:320)
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:107)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1417)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1158)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458)
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:296)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
	- locked <0x00000000c01b17b0> (a java.util.concurrent.ConcurrentHashMap)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:293)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:320)
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:107)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1417)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1158)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458)
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:296)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
	- locked <0x00000000c01b17b0> (a java.util.concurrent.ConcurrentHashMap)
...
"forked-thread-1":
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:207)
	- waiting to lock <0x00000000c01b17b0> (a java.util.concurrent.ConcurrentHashMap)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:293)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:198)
	at org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor.getAdvice(AbstractBeanFactoryPointcutAdvisor.java:86)
	- locked <0x00000000c17da860> (a java.lang.Object)
	at org.springframework.aop.framework.adapter.DefaultAdvisorAdapterRegistry.getInterceptors(DefaultAdvisorAdapterRegistry.java:79)
	at org.springframework.aop.framework.DefaultAdvisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(DefaultAdvisorChainFactory.java:62)
	at org.springframework.aop.framework.AdvisedSupport.getInterceptorsAndDynamicInterceptionAdvice(AdvisedSupport.java:482)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:190)
	at com.sun.proxy.$Proxy121.methodInvokedInTransaction(Unknown Source)
	at com.example.BeanA.methodInvokedInTransaction(BeanA.java:358)
	at com.example.BeanA.access$300(BeanA.java:44)
	at com.example.BeanA$1$1.doInTransaction(BeanA.java:91)
	at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:89)
	at com.example.BeanA$1.run(BeanA.java:86)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
	at java.lang.Thread.run(Thread.java:745)
Initialization of BeanA occurs before initialization of BeanB. The forked thread try to get an "org.springframework.transaction.interceptor.TransactionInterceptor#0" singleton instance while the main thread has already acquired a lock on singletonObjects map of an org.springframework.beans.factory.support.DefaultSingletonBeanRegistry instance because some other bean with a default bean scope which is singleton is higher in the bean hierarchy.
public class BeanA {    
	...
	
    @Override
    public void afterPropertiesSet() throws Exception {
        executor.execute(new Runnable() {
            @Override
            public void run() {
                TransactionTemplate tt = new TransactionTemplate(transactionManager);
                tt.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW);
                tt.execute(new TransactionCallback<Object>() {
                    @Override
                    public Object doInTransaction(TransactionStatus status) {
                        return methodInvokedInTransaction();
                    }
                });
            }
        });
    }
}

Is it a Spring TX bug or a client code should take care of this situation?

As a workaround I added```
depends-on="org.springframework.transaction.interceptor.TransactionInterceptor#0"

```org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(String, Class<T>, Object[], boolean)
```will use an already created TransactionInterceptor singleton instance instead of creating a new one.
Using```
@Transactional(propagation = Propagation.REQUIRED)
```or
```@Transactional(propagation = Propagation.REQUIRES_NEW)
```on the BeanA.afterPropertiesSet() method doesn't work.

Affects: 3.2.17, 4.2.6, 4.3 GA

Issue Links:

Referenced from: commits 0d3a22c, fd9d518, a1c0987

Backported to: 4.2.7, 3.2.18

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Jun 22, 2016

Juergen Hoeller commented

This is quite analogous to the recently fixed #18814 but in a separate AOP code path. In any case, fixed for 4.3.1 now, and to be backported to 4.2.7 (and later on to 3.2.18). Please give the upcoming 4.3.1.BUILD-SNAPSHOT a try and let me know whether it works for you there...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: data Issues in data modules (jdbc, orm, oxm, tx) status: backported An issue that has been backported to maintenance branches type: bug A general bug
Projects
None yet
Development

No branches or pull requests

2 participants