前言
最近啟動spring項目的時候遇到一個死鎖問題,使用jstack獲取線程堆棧的時候,可以看到2個線程出現了死鎖:
解決過程:
defaultsingletonbeanregistry.getsingleton()
源碼如下,可以看到這個方法需要對singletonobjects加鎖
第二處xxx.subject.core.cache.datalocalcacheinit.afterpropertiesset源碼如下:
可以看到:這個bean在初始化的時候,會開啟線程,調用另外一個bean的initdata()
方法從數據庫加載數據。等數據加載完畢,datalocalcacheinit這個bean的初始化才算完成。
通過上面的堆棧可以看出:spring容器在初始化bean的時候,會對singletonobjects對象加鎖;我們自己在afterpropertiesset()
方法中開啟了一個線程,最終也會觸發spring加載另外的bean。第一個線程(初始化spring的main線程)還沒有釋放鎖,第二個線程(自己開啟的線程),也需要獲取singletonobjects對象鎖,這樣就出現了死鎖。表現出來的現象就是:spring容器卡在那里,不能完成所有bean的初始化。
來看一段例子,這個例子和我們項目中實際代碼很相似。firstbean調用confighelper中的方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
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代碼如下:通過beanfactory獲取到另外一個bean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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代碼很簡單如下:
1
2
3
4
5
|
public class secondbean { public void say() { system.out.println( "secondbean...." ); } } |
spring配置文件和啟動代碼如下,運行可以發現出現死鎖:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<?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初始化的時候,如果我們在spring提供的一些擴展點處(beanfactoryaware/initializingbean等),開啟線程去獲取bean,很容器出現死鎖。因為spring初始化單例bean(大多數bean都是單例的)會加鎖。如果初始化1個bean的時候,還沒有釋放鎖,另一個線程再次觸發spring加載bean,就會出現死鎖。
解決上面的問題很簡單:firstbean邏輯上是依賴于confighelper和secondbean的,但是我們卻并沒有顯示地告訴spring這種邏輯關系。spring初始化firstbean的時候,進入afterpropertiesset()
,這個方法開啟了線程會觸發另外2個bean的加載。我們只要顯示地告訴spring這種依賴關系,讓spring先加載confighelper和secondbean就可以了。
1
2
3
|
<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> |
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對服務器之家的支持。
原文鏈接:https://blog.csdn.net/aitangyong/article/details/53036913