对J2EE中死锁问题的研究 - 编程入门网
alue(key);
synchronized(this) {
cache.put(key, value);
}
return value;
}
既然现在我们知道了会发生此死锁情况,就可以使用Thread.holdsLock()向queryForValue方法添加检查以尝试避免死锁情况:
上例中的Thread.holdsLock()很有用,但是只有在我们知道需要留心哪个锁时它才会发挥作用。如果有一个类似的方法可以确定当前线程占有哪个Java虚拟机锁,那么会很有用。任何执行任何种类的RPC调用、数据库访问等的代码片段都可以抛出异常或记录警告,指示在占有Java虚拟机锁时执行这些操作会有危险。 注意:虽然我们修复了上例中的死锁问题,但它仍有缺陷,因为在提交updateData的事务之前清空了缓存。如果在调用clearCache后、提交updateData事务前出现缓存缺失,则该缓存将加载旧数据,因为新数据尚未可见。这里的修复方法是仅在提交更改后清空缓存。注意,这只在MVCC数据库中发生。在基于锁的数据库中,挂起的update将阻塞缓存的读操作,所以在提交update的事务后缓存才能读取正确值。 经验法则 下面的这些指导可以帮您避免死锁问题,或者至少在出现死锁时能诊断并修复它们。 保持事务简短。 了解数据库锁行为(以及事务分离层)。 假定任何数据库访问都有可能陷入数据库死锁状况,但是能正确重试。 事务完成前不要更新任何非事务状态(内存状态、缓存等)。 确保在峰值并发时有足够大的资源池。 尝试不在同一时刻获取多个资源。如果必需,则按相同的顺序每次获取一个资源。 了解如何从应用服务器获取完整的线程转储以及从数据库获取数据库连接列表(包括互相阻塞的连接),知道每个数据库连接与哪个Java线程相关联。了解Java线程和数据库连接之间映射的最简单方法是向连接池访问模式添加日志记录功能。 当进行嵌套的EJB调用时,了解哪些调用使用与调用方同样的数据库连接。即使嵌套的调用运行在同一个全局事务中,它仍将使用不同的数据库连接,而这会导致跨资源死锁。 避免执行数据库调用和EJB调用,或在占有Java虚拟机锁时,执行其他与Java虚拟机无关的操作。如果有需要留心的特定Java虚拟机锁,就使用assert(!Thread.holdsLock(...)),从而避免以后的代码更改不会在无意间违背此规则。 结束语 J2EE应用程序中的跨资源死锁是一个大问题——它能导致整个应用程序慢慢终止,还很难被分离和修复,尤其是当开发人员不熟悉如何分析死锁环境的时候。我们讨论的情形将有助于您理解一些常见的死锁情形,并为您提供查找死锁的思路。更重要的是,我们概括的经验法则提供了一些要在代码中遵守的惯例,从而避免所有类似的死锁问题。 |
凌众科技专业提供服务器租用、服务器托管、企业邮局、虚拟主机等服务,公司网站:http://www.lingzhong.cn 为了给广大客户了解更多的技术信息,本技术文章收集来源于网络,凌众科技尊重文章作者的版权,如果有涉及你的版权有必要删除你的文章,请和我们联系。以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢! |