重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
小编给大家分享一下如何解决spring容器初始化遇到的死锁问题,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!
我们提供的服务有:网站建设、网站设计、微信公众号开发、网站优化、网站认证、蔡甸ssl等。为上千企事业单位解决了网站和推广的问题。提供周到的售前咨询和贴心的售后服务,是有科学管理、有技术的蔡甸网站制作公司
前言
最近启动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中的方法:
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配置文件和启动代码如下,运行可以发现出现死锁:
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就可以了。
以上是“如何解决spring容器初始化遇到的死锁问题”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注创新互联行业资讯频道!