spring容器初始化遇到的死鎖問(wèn)題解決
前言
最近啟動(dòng)spring項(xiàng)目的時(shí)候遇到一個(gè)死鎖問(wèn)題,使用jstack獲取線程堆棧的時(shí)候,可以看到2個(gè)線程出現(xiàn)了死鎖:

解決過(guò)程:
DefaultSingletonBeanRegistry.getSingleton()源碼如下,可以看到這個(gè)方法需要對(duì)singletonObjects加鎖

第二處xxx.subject.core.cache.DataLocalcacheInit.afterPropertiesSet源碼如下:

可以看到:這個(gè)bean在初始化的時(shí)候,會(huì)開(kāi)啟線程,調(diào)用另外一個(gè)bean的initData()方法從數(shù)據(jù)庫(kù)加載數(shù)據(jù)。等數(shù)據(jù)加載完畢,DataLocalcacheInit這個(gè)bean的初始化才算完成。
通過(guò)上面的堆棧可以看出:spring容器在初始化bean的時(shí)候,會(huì)對(duì)singletonObjects對(duì)象加鎖;我們自己在afterPropertiesSet()方法中開(kāi)啟了一個(gè)線程,最終也會(huì)觸發(fā)spring加載另外的bean。第一個(gè)線程(初始化spring的main線程)還沒(méi)有釋放鎖,第二個(gè)線程(自己開(kāi)啟的線程),也需要獲取singletonObjects對(duì)象鎖,這樣就出現(xiàn)了死鎖。表現(xiàn)出來(lái)的現(xiàn)象就是:spring容器卡在那里,不能完成所有bean的初始化。
來(lái)看一段例子,這個(gè)例子和我們項(xiàng)目中實(shí)際代碼很相似。FirstBean調(diào)用ConfigHelper中的方法:
public class FirstBean implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("first bean is initializing....");
BlockingQueue queue = new ArrayBlockingQueue(10);
Thread thread = new Thread() {
@Override
public void run() {
ConfigHelper.doSomething();
queue.add(1);
}
};
thread.start();
queue.take();
System.out.println("first get data....");
}
}
ConfigHelper代碼如下:通過(guò)BeanFactory獲取到另外一個(gè)bean
public class ConfigHelper implements BeanFactoryAware {
private static BeanFactory factory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.factory = beanFactory;
}
public static void doSomething()
{
SecondBean bean = (SecondBean)factory.getBean("second");
bean.say();
}
}
SecondBean代碼很簡(jiǎn)單如下:
public class SecondBean {
public void say() {
System.out.println("SecondBean....");
}
}
spring配置文件和啟動(dòng)代碼如下,運(yùn)行可以發(fā)現(xiàn)出現(xiàn)死鎖:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="config" class="net.aty.spring.deadlock.ConfigHelper"></bean>
<bean id="first" class="net.aty.spring.deadlock.FirstBean"></bean>
<bean id="second" class="net.aty.spring.deadlock.SecondBean"></bean>
</beans>
public class Main {
public static void main(String[] args) {
ApplicationContext context = new FileSystemXmlApplicationContext(
"src/main/java/net/aty/spring/deadlock/deadlock.xml");// 加載 spring 配置文件
}
}

spring初始化的時(shí)候,如果我們?cè)趕pring提供的一些擴(kuò)展點(diǎn)處(BeanFactoryAware/InitializingBean等),開(kāi)啟線程去獲取bean,很容器出現(xiàn)死鎖。因?yàn)閟pring初始化單例bean(大多數(shù)bean都是單例的)會(huì)加鎖。如果初始化1個(gè)bean的時(shí)候,還沒(méi)有釋放鎖,另一個(gè)線程再次觸發(fā)spring加載bean,就會(huì)出現(xiàn)死鎖。
解決上面的問(wèn)題很簡(jiǎn)單:FirstBean邏輯上是依賴于ConfigHelper和SecondBean的,但是我們卻并沒(méi)有顯示地告訴spring這種邏輯關(guān)系。spring初始化FirstBean的時(shí)候,進(jìn)入afterPropertiesSet() ,這個(gè)方法開(kāi)啟了線程會(huì)觸發(fā)另外2個(gè)bean的加載。我們只要顯示地告訴spring這種依賴關(guān)系,讓spring先加載ConfigHelper和SecondBean就可以了。
<bean id="config" class="net.aty.spring.deadlock.ConfigHelper" depends-on="second"></bean> <bean id="first" class="net.aty.spring.deadlock.FirstBean" depends-on="config"></bean> <bean id="second" class="net.aty.spring.deadlock.SecondBean"></bean>

總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
基于java構(gòu)造方法Vector創(chuàng)建對(duì)象源碼分析
這篇文章主要介紹了java構(gòu)造函數(shù)中對(duì)Vector源碼及原理的分析,有需要的朋友可以借鑒參考下,希望可以有所幫助,祝大家早日升職加薪2021-09-09
MyBatis-Plus中Service接口的lambdaUpdate用法及實(shí)例分析
本文將詳細(xì)講解MyBatis-Plus中的lambdaUpdate用法,并提供豐富的案例來(lái)幫助讀者更好地理解和應(yīng)用該特性,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-03-03
Java開(kāi)發(fā)HashMap?key必須實(shí)現(xiàn)hashCode?equals方法原理
這篇文章主要為大家介紹了Java開(kāi)發(fā)HashMap?key必須實(shí)現(xiàn)hashCode?equals方法原理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
Java實(shí)現(xiàn)字符串和輸入流的相互轉(zhuǎn)換
這篇文章主要介紹了Java實(shí)現(xiàn)字符串和輸入流的相互轉(zhuǎn)換方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08
詳解Java如何實(shí)現(xiàn)有效的并發(fā)處理
隨著互聯(lián)網(wǎng)的蓬勃發(fā)展,現(xiàn)代軟件系統(tǒng)對(duì)于并發(fā)性能的要求越來(lái)越高,如何學(xué)習(xí)和掌握并發(fā)編程技術(shù)成為了Java開(kāi)發(fā)人員必備的技能之一,本文主要介紹了Java并發(fā)編程的相關(guān)概念、原理和實(shí)踐技巧,感興趣的可以了解下2023-11-11
解決SpringBoot2多線程無(wú)法注入的問(wèn)題
這篇文章主要介紹了解決SpringBoot2多線程無(wú)法注入的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-08-08
java教程散列表和樹(shù)所對(duì)應(yīng)容器類及HashMap解決沖突學(xué)習(xí)
本篇篇文章是java教程,主要介紹了java教程散列表,樹(shù)所對(duì)應(yīng)容器類及HashMap解決沖突的學(xué)習(xí),有需要的朋友可以借鑒參考下,希望能夠有所幫助2021-10-10

