首页 | 资讯动态 | linux基础 | 系统管理 | 网络管理 | 编程开发 | linux数据库 | 服务器技术 | linux相关 | linux认证 | 嵌入式 | 下载中心 | 专题 | linux招聘 | 镜像站
OKLinux中文技术站
·设为首页
·加入收藏
·联系我们
系统管理: 中文环境 系统管理 桌面应用 内核技术 | Linux基础: 基础入门 安装配置 常用命令 经验技巧 软件应用 | Linux数据库: Mysql Postgre Oracle DB2 Sybase other
网络管理: 网络安全 网络应用 Linux服务器 环境配置 黑客安全 | 编程开发: PHP CC++ Python Perl Shell 嵌入式开发 java jsp | PHP技术: PHP基础 PHP技巧 PHP应用 PHP文摘
Linux资讯 Linux招聘 Linux专题 Apache | Linux相关: 硬件相关 Linux解决方案 Linux认证 企业应用 其它Unix | 相关下载: 资料下载 参考手册 开发工具 服务器类 软路由 其它
 技术搜索:
会员中心 注册会员 高级搜索  
  → 当前位置:首页>编程开发>java>java企业应用>正文

以全局的固定顺序获取多个锁来避免死锁

http://www.oklinux.cn  2008-03-07  来源: ccidnet 执木  会员收藏  游客收藏  【 】 

当两个或多个线程互相等待时被阻塞,就会发生死锁。例如,第一个线程被第二个线程阻塞,它在等待第二个线程持有的一个资源。而第二个线程在获得第一个线程持有的某个资源之前不会释放这个资源。由于第一个线程在获得第二个线程持有的那个资源之前不会释放它自己所持有的资源,而第二个线程在获得第一个线程持有的一个资源之前也不会释放它所持有的资源,于是这两个线程就被死锁。


在编写多线程代码时,死锁是最难处理的问题之一。因为死锁可能在最意想不到的地方发生,所以查找和修正它既费时又费力。例如,试考虑下面这段锁定了多个对象的代码。





public int sumArrays(int[] a1, int[] a2)
{
int value = 0;
int size = a1.length;
if (size == a2.length) {
synchronized(a1) { //1
synchronized(a2) { //2
for (int i=0; i value = a1[i] a2[i];
}
}
}
return value;
}


这段代码在求和操作中访问两个数组对象之前正确地锁定了这两个数组对象。它形式简短,编写也适合所要执行的任务;但不幸的是,它有一个潜在的问题。这个问题就是它埋下了死锁的种子,除非您在不同的线程中对相同的对象调用该方法时格外小心。要查看潜在的死锁,请考虑如下的事件序列:



  1. 创建两个数组对象,ArrayAArrayB


  2. 线程 1 用下面的调用来调用 sumArrays 方法:
    sumArrays(ArrayA, ArrayB);


  3. 线程 2 用下面的调用来调用 sumArrays 方法:
    sumArrays(ArrayB, ArrayA);


  4. 线程 1 开始执行 sumArrays 方法并在 //1 处获得对参数 a1 的锁,对于这个调用而言,它就是对 ArrayA 对象的锁。


  5. 然后在 //2 处,在线程 1 获得对 ArrayB 的锁之前被抢先。


  6. 线程 2 开始执行 sumArrays 方法并在 //1 处获得对参数 a1 的锁,对于这个调用而言,它就是对 ArrayB 对象的锁。


  7. 然后线程 2 在 //2 处试图获取对参数 a2 的锁,它是对 ArrayA 对象的锁。因为这个锁当前由线程 1 持有,所以线程 2 被阻塞。


  8. 线程 1 开始执行并在 //2 处试图获取对参数 a2 的锁,它是对 ArrayB 对象的锁。因为这个锁当前由线程 2 持有,所以线程 1 被阻塞。


  9. 现在两个线程都被死锁。

避免这种问题的一种方法是让代码按固定的全局顺序获取锁。在本例中,如果线程 1 和线程 2 按相同的顺序对参数调用 sumArrays 方法,就不会发生死锁。但是,这一技术要求,多线程代码的程序员在调用那些锁定作为参数传入的对象的方法时需要格外小心。在您遇到这种死锁并不得不进行调试之前,使用这一技术的应用程序似乎不切实际。


另外,您也可以将锁定顺序嵌入对象的内部。这允许代码查询它准备为其获得锁的对象,以确定正确的锁定顺序。只要即将锁定的所有对象都支持锁定顺序表示法,并且获取锁的代码遵循这一策略,就可避免这种潜在死锁的情况。


在对象中嵌入锁定顺序的缺点是,这种实现将使内存需求和运行时成本增加。另外,在上例中应用这一技术需要在数组中有一个包装对象,用来存放锁定顺序信息。例如,试考虑下面的代码,它由前面的示例修改而来,其中实现了锁定顺序技术:





class ArrayWithLockOrder
{
private static long num_locks = 0;
private long lock_order;
private int[] arr;

public ArrayWithLockOrder(int[] a)
{
arr = a;
synchronized(ArrayWithLockOrder.class) {
num_locks ; // 锁数加 1。
lock_order = num_locks; // 为此对象实例设置唯一的 lock_order。
}
}
public long lockOrder()
{
return lock_order;
}
public int[] array()
{
return arr;
}
}

class SomeClass implements Runnable
{
public int sumArrays(ArrayWithLockOrder a1,
ArrayWithLockOrder a2)
{
int value = 0;
ArrayWithLockOrder first = a1; // 保留数组引用的一个
ArrayWithLockOrder last = a2; // 本地副本。
int size = a1.array().length;
if (size == a2.array().length)
{
if (a1.lockOrder() > a2.lockOrder()) // 确定并设置对象的锁定
{ // 顺序。
first = a2;
last = a1;
}
synchronized(first) { // 按正确的顺序锁定对象。
synchronized(last) {
int[] arr1 == a1.array();
int[] arr2 == a2.array();
for (int i=0; i value = arr1[i] arr2[i];
}
}
}
return value;
}
public void run() {
//...
}
}


在第一个示例中,ArrayWithLockOrder 类是作为数组的一个包装提供的。每创建该类的一个新对象,该类就将 static num_locks 变量加 1。一个单独的 lock_order 实例变量被设置为 num_locks static 变量的当前值。这可以保证,对于该类的每个对象,lock_order 变量都有一个独特的值。

共2页: 上一页 1 [2] 下一页

上一篇:JAVA高级:Java中限时线程回调方式的实现   下一篇:进阶:Java Reflection (JAVA反射)详解


收藏于收藏夹】 【评论】 【推荐】 【打印】 【关闭
相关文档
·进阶:Java Reflection (JAVA反射)详解
·JAVA高级:Java中限时线程回调方式的实现
·超线程多核心下Java多线程编程彻底分析
·JBuilder7 Weblogic7 mysql开发EJB配置
·在Web项目中用到JNI时应该注意的问题
·开发JAVA编程中字符串分割的两种方法
·表现层框架Struts/Tapestry/JSF比较
·Java理论和实践:用软引用阻止内存泄漏
·JAVA入门基础:区分引用类型和原始类型
·Java SE6调用Java编译器的两种新方法
·学习心得 - JSP处理异常及一些高级主题
·Java程序开发过程中异常处理的特殊情况
·架构与模式:多线程有几种常用的编程模型
·建立JSP操作用以提高数据库访问的效率
·使用JAVA中的动态代理实现数据库连接池
·Weblogic下使用jsp查询的Entity Bean
发表评论
密码: 匿名评论
评论内容:

(不超过250字,需审核后才会公布,请自觉遵守互联网相关政策法规)
 
  最新文档
·Java SE6调用Java编译器的两种新方法
·表现层框架Struts/Tapestry/JSF比较
·在Web项目中用到JNI时应该注意的问题
·JBuilder7 Weblogic7 mysql开发EJB配置
·进阶:Java Reflection (JAVA反射)详解
·JAVA高级:Java中限时线程回调方式的实
·超线程多核心下Java多线程编程彻底分析
·开发JAVA编程中字符串分割的两种方法
·Java理论和实践:用软引用阻止内存泄漏
·JAVA入门基础:区分引用类型和原始类型
·学习心得 - JSP处理异常及一些高级主题
·Java程序开发过程中异常处理的特殊情况
  阅读排行
·Commons-logging Log4j 的入门指南
·进阶:Java Reflection (JAVA反射)详解
·服务器及中间件:TomCat 多虚拟站点配置
·开发框架:一篇关于SSH架构的简单总结
·使用JAVA中的动态代理实现数据库连接池
·数据库相关:Log4j和JDBMonitor的比较
·Java语言:敏捷开发技巧-消除代码异味
·架构与模式:多线程有几种常用的编程模
·Java SE6调用Java编译器的两种新方法
·jboss4.0下使用MySql数据源的设置方法
·开发框架-Struts里过滤器的简单使用
·表现层框架Struts/Tapestry/JSF比较
·在Web项目中用到JNI时应该注意的问题
·Java 安全:Java 语言的 XML 验证 API
·Hibernate源码中几个包的作用简要介绍
网摘收藏: