- 浏览: 375065 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
真的全站唯一:
描述的能不能准确一点,我也以为bigDecimal性能比dou ...
【性能】Java BigDecimal和double性能比较 -
zhanggang807:
学习到了。。以后会考虑往这方面设计
【java规范】Java spi机制浅谈 -
Xiong506:
xiyuan1025 写道你这是在linux下吗,我在linu ...
[监控]Btrace监控简单笔记 -
Xiong506:
xiyuan1025 写道你这是在linux下吗,我在linu ...
[监控]Btrace监控简单笔记 -
Bll:
找不到实现类
【java规范】Java spi机制浅谈
1.背景
上周在生产环境应用启动时,发生应用频频发生死锁的现象。原因是因为 spring IOC 容器还未初始化完成,就有工作线程调用 context.getBean() 来获取容器里的对象。具体产生死锁的原因条件有:
1. 应用启动的时候 Main 线程进行 spring 容器初始化。
2. 容器初始化的过程中有工作线程也起来了并开始工作。
3. 工作线程代码里显式调用 spring ioc 容器的 context.getBean(String beanName) 。
4. 工作线程显式获取的 bean 未实例化,且里存在直接或者间接的注解注入方式的情况。
以上情况都符合,那工作线程和 main 线程可能发生死锁。
2.具体原因分析
Spring ioc 容器组合里有两个重要的 map :
/** Map of bean definition objects, keyed by bean name */
private final Map beanDefinitionMap = CollectionFactory.createConcurrentMapIfPossible (16);
//bean definition 是 spring 容器里描述 bean 对象的元数据( bean 的创建等就是基于此来创建)。 Spring 容器初始化实例之前需要先把配置文件的 bean 定义都转化成内部的统一描述对象 BeanDefinition 。该 beanDefinitionMap 用于保存这些数据。
/** Cache of singleton objects: bean name --> bean instance */
private final Map singletonObjects = CollectionFactory.createConcurrentMapIfPossible (16);
//spring 容器用于 cache 住 spring 容器初始化的单例对象
以上两个对象为了保证数据的一致性,在操作的时候很多时候会进行加锁。如以下两个过程。
过程一: spring 容器初始化
Spring 容器初始化的时候会实例化所有单例对象( preInstantiateSingletons ),这个过程中会对上面两个对象加锁,以防止并发。先对 beanDefinitionMap 加锁,防止元数据被修改,然后在每次实例化单例对象的时候对 singletonObjects 加锁,防止并发修改。
过程二:根据 spring 容器获取一个单例对象。
调用 spring 容器的 context.getBean ( beanName ),如果该 bean 是单例且还未实例化,这个时候就需要进行实例化,如果该 bean 直接或间接存在注解方式的 bean 注入的时候,过程中也会对以上两个对象进行加锁防止并发。先对 singleObjects 加锁,从改 map 里找是否有存在 beanName 的对象,没有的话在创建该 bean 的过程中会对 beanDefinitionMap 加锁。
可以看出以上过程一和过程二对两个对象的锁顺序是不一致的,所以并发执行就可能会发生死锁。
在本机写了一个简单的实验,死锁的线程栈信息可以证明这一点,具体如下:
代码十分简单,如下:
package com.alibaba.test; import org.springframework.context.support.ClassPathXmlApplicationContext; public class DeadLockTest { ClassPathXmlApplicationContext context = null; public void startContext() { context = new ClassPathXmlApplicationContext(); context.setConfigLocation("spring/bean/mybeans.xml"); context.refresh(); } public void getBean() { new Thread(new GetBean()).start(); } /** * @param args */ public static void main(String[] args) { DeadLockTest t = new DeadLockTest(); //get bean 工作线程 t.getBean(); //容器启动主线程 t.startContext(); } //get bean 工作线程 class GetBean implements Runnable { public void run() { try { Thread.sleep(10000); MyBean myBean = (MyBean) context.getBean("myBean"); myBean.getOtherBean().sayHello(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // TODO Auto-generated method stub } } }
其中mybean定义如下(一定要用注解的注入方式,才会有可能发生死锁!):
package com.alibaba.test; import org.springframework.beans.factory.annotation.Autowired; public class MyBean { @Autowired private OtherBean otherBean; public OtherBean getOtherBean() { return otherBean; } public void setOtherBean(OtherBean otherBean) { this.otherBean = otherBean; } public void sayHello() { otherBean.sayHello(); } }
myBean.xml:
<?xml version="1.0" encoding="GB2312"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd"> <beans default-autowire="byName"> <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" /> <bean id="myBean" class="com.alibaba.test.MyBean" /> </beans>
以上代码经过调试控制,即会发生思索,控制如下:
1.main线程在DefaultListableBeanFactory.preInstantiateSingletons 方法的
synchronized (this.beanDefinitionMap) {
...
}里加个断点
2.getBean工作线程在DefaultSingletonBeanRegistry.getSingleton(String beanName, ObjectFactory singletonFactory)方法的
synchronized (this.singletonObjects) {
...
}里加个断点。
3.等1,2断点都进去之后,再触发继续运行,就会发生死锁。
结论也许可以算是 spring 的 bug ,也许可以算我们使用不当。
总之,有两点需要注意:
1. 尽量避免显式调用 ioc 容器,注入工作由容器自己来完成。
2. 尽量在容器初始化完,开始对外服务。
评论
如果在spring的bean初始话init参数配置的afterPropertiesSet这个方法里另外开启一个线程
这样也可能发生死锁了?
class beaniNit inplenmens InitializingBean,BeanFactoryAware{
beanFactory=null;
afterPropertiesSet(){
beanFactory.getBean("myBean").start();///这个地方如果新开线程取的话,也可能发生死锁!!
}
setBeanFactory(beanFactory ){
this.beanFactory= beanFactory;
}
}
发表评论
-
【java规范】Java spi机制浅谈
2012-04-24 23:04 45714最近看到公司的一些框架和之前看到的开源的一些框架的一些服务发现 ... -
Xml ResourceBundle简单实现
2012-04-17 21:45 4364ResourceBundle主要是用于和本地语言环境相关的一些 ... -
【maven】多子模块maven模板工程archetype创建过程
2012-04-02 20:55 17573最近项目里需要创建一 ... -
【java基础】如何设计java应用程序的平滑停止
2012-03-05 23:44 10924java应用程序退出的触发机制有: 1.自动结束:应用没有存 ... -
【java并发】juc Executor框架详解
2012-02-26 13:55 12374Executor 框架是 juc 里提供的线程池的实现。 ... -
【java并发】juc高级锁机制探讨
2012-02-23 00:52 8510最近在看一些j ... -
【java并发】基于JUC CAS原理,自己实现简单独占锁
2012-02-14 13:47 7765synchronized的基本原理回 ... -
[NoSQL]MongoDB初体验
2012-01-05 16:06 3925因为未来业务发展的一 ... -
【JDBC,数据库】Oracle date和timestamp类型混用时需要注意的索引失效问题
2011-12-14 15:27 88531.关于 Oracle date和timestamp类型 D ... -
【JVM】HotSpot JVM内存管理和GC策略总结
2011-12-13 22:05 15878JVM的相关知识是学习java ... -
【性能】JDBC PreparedStatement和连接池PreparedStatement Cache学习记录
2011-12-08 17:20 16759之前看JDBC规范的时候对PreparedStatement只 ... -
32位机器下的一个java.lang.OutOfMemoryError错误分析
2011-10-17 11:19 2546昨天在本人windows机器( ... -
【Spring】IOC核心源码学习(三):bean标签和自定义标签实现原理
2011-09-25 11:13 9619接上文: 【Spring】IOC核心源码学习(二):容器初始 ... -
【Spring】IOC核心源码学习(二):容器初始化过程
2011-09-21 21:03 23508接上文 啃啃老菜: Spring IOC核心源码学习(一 ... -
[监控]Btrace监控简单笔记
2011-09-09 10:57 4902前阵子看了公司网站的一个cache 命中率统计的btrace监 ... -
DBCP数据源配置项记录
2011-09-01 20:22 2933网站最近发生了数据库连接爆掉的问题。排查了下各个应用存在 ... -
【性能】Java BigDecimal和double性能比较
2011-08-28 20:06 14107我们知道 java 里面有个 BigDecimal ... -
JDK7 AIO 初体验
2011-08-17 19:20 2534JDK7 AIO初体验 JDK7已经releas ... -
啃啃老菜:Spring IOC核心源码学习(一)
2011-08-14 13:57 7446啃啃老菜: Spring IOC核心源码 ... -
如果要用java实现算法,一定慎用递归
2011-04-06 20:41 12823现象 : 递归是我们很经典的一种算法实现,可以很好的 ...
相关推荐
spring Ioc容器配置 IOC容器数据源配置 <!-- 配置数据源 --> destroy-method="close"> <value>org.gjt.mm.mysql.Driver <value>jdbc:mysql://localhost:3306/demo <value>root ...
spring ioc容器初始化流程图 spring ioc容器依赖注入流程图 spring aop实现原理流程图
Java-Spring-SpringIoC容器-SpringIoC的学习 SpringIoC容器的学习笔记 主要介绍了IoC容器工作原理以及如何配置IoC容器 通过标签和注解配置容器
自己动手实现Spring IoC容器, 写Spring IOC容器.wps写Spring IOC容器.wps写Spring IOC容器.wps
Spring IoC容器实现的结构分析
迷你书是《Spring揭秘》的精选版,节选了原书中介绍Spring IoC容器的6个章节。《Spring揭秘》以幽默生动的语言、辅以有趣的故事和典故,循循善诱地阐述了Spring框架的方方面面。针对Spring框架的主要功能以及开发者...
spring ioc容器部署实现
熟练使用Spring IOC容器,如有意见或者建议
讲的非常不错的Spring书籍,特别有利于对IOC容器的理解,特别推荐哦
spring IOC容器依赖注入XML配置详解 运行环境:eclipse 构建工具:maven 不提供maven构建,maven用来解决jar包的依赖
Spring的IoC容器初始化源码解析,包括资源定位、加载、注册3个过程
仿照Spring写的一个Ioc容器,注释很详细!
NULL 博文链接:https://ziyoujiedao.iteye.com/blog/262360
基于xml注册,简单模拟spring ioc容器。快速理解ioc容器
主要介绍了Spring IOC 容器启动流程分析,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
spring ioc指的是控制反转,IOC容器负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。交由Spring容器统一进行管理,从而实现松耦合
主要为大家详细解析了Spring中IoC优点与缺点,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
使用spring的Ioc容器组装各种组件总结
spring的ioc容器详细讲解,理解springioc容器
Spring的IoC容器的详细介绍,想了解Spring核心的人不可不看~