对一个死锁问题的思考
前些天周杰让我看了一段代码, 让我觉得学习一下基础的知识还是挺重要的, 对理解代码有很大帮助。
这是一个关于死锁的问题,代码如下:
很明显,这段代码在多线程情况下,会产生死锁:
假设线程1 做的操作是账户A给账户B转账, 先锁住了A账户, 接下来试图申请B账户的锁,
与此同时线程2 在从 账户B给账户A 转账, 先锁住了B账户的锁, 接下来试图申请A账户的锁。
两个线程各自持有资源, 然后等待获取对方的资源, 都无法执行下去, 死锁就出现了。
怎么写代码才能避免这种死锁呢?下面的代码是一种解决办法:
这段代码看起来有点吃力,但是如果你学过操作系统对死锁的处理的话, 就变的很容易。
操作系统中的理论是这样的: 如果有多个线程对多个资源进行访问时, 需要对资源进行排序(排序的方法你自己确定), 然后所有的线程都按同样的次序来访问资源,这样就不会出现环路等待了。
例如有10个线程, 每个都要访问多个资源, 对资源排序以后, 大家都先锁住1号资源进行访问(注意同一时刻,只有一个线程能获得锁, 别的都得等待) 然后是2号, 3号。。。
由于大家访问的次序是一样的, 就不会出现死锁的的情况。
理解了这一点, 对于上面的代码就很容易理解了, 实际就是对Account(账户)进行资源的排序, 通过 Java 内置的方法,得到Hashcode。 然后按顺序访问。
如果fromAccount 比较小, 那就先锁住fromAccount, 然后锁住toAccount, 反过来也是类似。
假设线程1 做的操作是账户A给账户B转账, 先锁住了A账户(假设A的hashcode 比较小), 接下来试图申请B账户的锁,
与此同时线程2 在从 账户B给账户A 转账, 由于A的hashcode 较小, 这个线程也试图先申请A的锁, 当然它申请不到, 因为已经被线程1持有了, 线程2只能等待
等到线程1完成操作以后, 线程2才能继续, 死锁就消除了。
如果两个账号的hashCode 相等怎么办, 没办法,只好引用一个第三方的锁了解决了, 这就是上述代码的else 分支 synchronized(lock) .
所以你看, 那些基础的理论还是挺重要的,的确是前辈们总结出来的精华, 学会了以后, 你看问题的角度和眼界就不同了。
-------------------------------------------------------------------------------------------------------------
关于码农翻身公共号(coderising) : 由工作15年的IBM架构师刘欣创建,致力于帮助在校学生和刚工作的同学提高编程水平,多年的工作经验,肯定能让你少走弯路。
加入码农翻身QQ群:135769418 带着你做项目实战, 可是真的项目需求哦,涉及大量Android,iOS, Web开发的技术, 同学们做完后参加工作,技术上就不用愁了。
长按二维码, 关注"coderising"