一則spring容器啟動死鎖問題(DefaultListableBeanFactory/DefaultSingletonBeanRegistry)


線上發現一個問題,應用在啟動時會卡死,log上並沒有什么異常輸出,初判應該是死鎖問題.
抓現場的thread dump文件, 確實是有兩個線程有deadlock問題.

線程一

"HSFBizProcessor-8-thread-13" daemon prio=10 tid=0x00007fc686a83000 nid=0x37128 waiting for monitor entry [0x000000004b7f3000]
   java.lang.Thread.State: BLOCKED (on object monitor)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinitionNames(DefaultListableBeanFactory.java:192)
	- waiting to lock <0x00000007707d84c8> (a java.util.concurrent.ConcurrentHashMap)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:209)
	at org.springframework.beans.factory.BeanFactoryUtils.beanNamesForTypeIncludingAncestors(BeanFactoryUtils.java:187)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:652)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:610)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:412)
	at org.springframework.beans.factory.annotation.InjectionMetadata.injectFields(InjectionMetadata.java:105)
	at 

線程二

"main":
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:180)
	- waiting to lock <0x00000007707ae6c0> (a java.util.concurrent.ConcurrentHashMap)
	at org.springframework.beans.factory.support.AbstractBeanFactory.isFactoryBean(AbstractBeanFactory.java:747)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:422)
	- locked <0x00000007707d84c8> (a java.util.concurrent.ConcurrentHashMap)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:728)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:380)
	- locked <0x00000007707d7fc8> (a java.lang.Object)
	at org.springframework.web.context.ContextLoader.createWebApplicationContext(ContextLoader.java:255)
	at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:199)

棧文件的結尾已經指出了兩個線程在競爭什么鎖,

  which is held by "main"
"main":
  waiting to lock monitor 0x00007fc681220a08 (object 0x00000007707ae6c0, a java.util.concurrent.ConcurrentHashMap),
  which is held by "HSFBizProcessor-8-thread-13"
"HSFBizProcessor-8-thread-13":
  waiting to lock monitor 0x00007fc686692438 (object 0x00000007707d84c8, a java.util.concurrent.ConcurrentHashMap),
  which is held by "main"

主要是線程HSFBizProcessor的DefaultListableBeanFactory.getBeanDefinitionNames(DefaultListableBeanFactory.java:192)
需要鎖對象0x00000007707d84c8, 而這個對象已經被main的DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:422)
鎖住了.

spring的版本是2.5.

這個問題的原因是spring在初始化singleton bean的時候,需要鎖兩個地方,第一個是
DefaultSingletonBeanRegistry中的singletonObjects:

/** Cache of singleton objects: bean name --> bean instance */
	private final Map singletonObjects = CollectionFactory.createConcurrentMapIfPossible(16);
        ...
		synchronized (this.singletonObjects) {
			Object singletonObject = this.singletonObjects.get(beanName);
			if (singletonObject == null) {
				...
				try {
					singletonObject = singletonFactory.getObject();
				}

singletonObjects是DefaultSingletonBeanRegistry中的並發Map.

第二步需要鎖DefaultListableBeanFactory中的beanDefinitionMap:

/** Map of bean definition objects, keyed by bean name */
private final Map beanDefinitionMap = CollectionFactory.createConcurrentMapIfPossible(16);
    ...
	public String[] getBeanDefinitionNames() {
		synchronized (this.beanDefinitionMap) {
			if (this.frozenBeanDefinitionNames != null) {
				return this.frozenBeanDefinitionNames;
			}
			...
		}
	}

如果在應用啟動時只有一個線程進入spring初始化bean時是沒問題的, 但這里應用代碼在spring的容器啟動的同時,有另外一個main方法同時開始運行調用spring的DefaultListableBeanFactory.preInstantiateSingletons方法,兩個線程兩把鎖,是有可能造成飢餓競爭的.

在spring容器外自行調用spring的創建bean方法要注意線程問題.

引申閱讀:
Spring Bean Creation is Not Thread Safe

A Java Thread deadlock has occured

Performance bottleneck and potential thread deadlock in DefaultSingletonBeanRegistry

Spring deadlocks between DefaultListableBeanFactory and DefaultSingletonBeanRegistry


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM