文章出處

線上發現一個問題,應用在啟動時會卡死,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


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()