java初始化锁,spring容器初始化遇到的死锁问题解决

前言

最近启动spring项目的时候遇到一个死锁问题,使用jstack获取线程堆栈的时候,可以看到2个线程出现了死锁:

04ae4a35e444a58ebcf9649bb987f977.png

解决过程:

DefaultSingletonBeanRegistry.getSingleton()源码如下,可以看到这个方法需要对singletonObjects加锁

7cbed363566a8260640fc81db843e68a.png

第二处xxx.subject.core.cache.DataLocalcacheInit.afterPropertiesSet源码如下:

a64a2b2e90707228fb3716bf48d6355e.png

可以看到:这个bean在初始化的时候,会开启线程,调用另外一个bean的initData()方法从数据库加载数据。等数据加载完毕,DataLocalcacheInit这个bean的初始化才算完成。

通过上面的堆栈可以看出:spring容器在初始化bean的时候,会对singletonObjects对象加锁;我们自己在afterPropertiesSet()方法中开启了一个线程,最终也会触发spring加载另外的bean。第一个线程(初始化spring的main线程)还没有释放锁,第二个线程(自己开启的线程),也需要获取singletonObjects对象锁,这样就出现了死锁。表现出来的现象就是:spring容器卡在那里,不能完成所有bean的初始化。

来看一段例子,这个例子和我们项目中实际代码很相似。FirstBean调用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代码如下:通过BeanFactory获取到另外一个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代码很简单如下:

public class SecondBean {

public void say() {

System.out.println("SecondBean....");

}

}

spring配置文件和启动代码如下,运行可以发现出现死锁:

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">

public class Main {

public static void main(String[] args) {

ApplicationContext context = new FileSystemXmlApplicationContext(

"src/main/java/net/aty/spring/deadlock/deadlock.xml");// 加载 spring 配置文件

}

}

cf08466999bf2d3b873c39e70e720788.png

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就可以了。

c40fb50e0b6dfa22edc2caa1ac91de59.png

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值