- 前言
- 一、锁顺序死锁
- 二、动态的锁顺序死锁。
提示:以下是本篇文章正文内容,下面案例可供参考
一、锁顺序死锁看下面代码,很容易造成死锁,leftRight和rightLeft方法分别获取了left和right锁,只不过二者获取锁的顺序是相反的。那么如果一个Thread调用了leftRight,另一个Thread调用了rightLeft,并且两个Thread的操作是交错执行的,那么就容易发生时所。
class LeftRightDeadlock {
private final Object left = new Object();
private final Object right = new Object();
public void leftRight() {
synchronized(left) {
synchronized(right) {
doSomethingA();
}
}
}
public void rightLeft() {
synchronized(right) {
synchronized(left) {
doSomethingB();
}
}
}
}
如下调用就能发生死锁
ThreadA :----->锁住left---->尝试锁住right--->永久等待。
ThreadB :----->锁住right--->尝试锁住left--->永久等待。
根本原因在于leftRight和rightLeft方法获取锁的顺序不一样。如果每个需要left锁和right锁的线程都以相同的顺序来获取left锁和right锁则不存存在此问题。所以如果所有线程以固定的顺序来获取锁,那么在程序中就不会存在因为顺序造成的死锁问题。
所以我们在这里将rightLeft改成跟leftRight一样的顺序即可:
public void rightLeft() {
synchronized(left) {
synchronized(right) {
doSomethingB();
}
}
}
二、动态的锁顺序死锁。
leftRight和rightLeft方法是两个固定写死的方法,所以修改他们很容易,只需要将顺序改变成一致即可。但是,如果是下面的代码呢?是否会发生死锁呢?
/**
* 转账方法
* @param fromAccount 谁转账
* @param toAccount 转账给谁
* @param amount 转账金额
*/
public void transferMoneyMoney(Account fromAccount,Account toAccount,DollarAmount amount) {
synchronized(fromAccount) {
synchronized(toAccount) {
//执行转账逻辑
}
}
}
transferMoneyMoney方法的作用就是用来转账,参数定了转账账户、被转账账户和转账金额。乍一看没问题,但是仍然会造成死锁,死锁的原因跟leftRight和rightLeft一模一样,都是获取锁的顺序造成的:
ThreadA :transferMoneyMoney(X,Y)----->锁住X---->尝试锁住Y-->永久等待。
ThreadB :transferMoneyMoney(Y,X)------>锁住Y--->尝试锁住X--->永久等待。
前面说过只要所有线程以固定的顺序获取锁,那么就不会发生死锁,所以我们改造的方向也有了,就是要达成如下效果,也就是获取所得顺序不应该跟参数的顺序有关,而是固定顺序:
ThreadA :transferMoneyMoney(X,Y)----->锁住X---->尝试锁住Y-->转账成功。
ThreadB :transferMoneyMoney(Y,X)------>锁住X--->尝试锁住Y--->转账成功。
有了这个思路我们就可以如下改造:
/**
* 转账方法
* @param fromAccount 谁转账
* @param toAccount 转账给谁
* @param amount 转账金额
*/
public void transferMoneyMoney(Account fromAccount,Account toAccount,DollarAmount amount) {
int fromHash = System.identityHashCode(fromAccount);
int toHash = System.identityHashCode(toHash);
if(fromHash>toHash) {
synchronized(fromAccount) {
synchronized(toAccount) {
//执行转账逻辑
}
}
}else if(fromHashyHash:
ThreadA :transferMoneyMoney(X,Y)----->锁住X---->尝试锁住Y-->转账成功。
ThreadB :transferMoneyMoney(Y,X)------>锁住X--->尝试锁住Y--->转账成功。
如果xHash锁住Y---->尝试锁住X-->转账成功。
ThreadB :transferMoneyMoney(Y,X)------>锁住Y--->尝试锁住X--->转账成功。
可以看出不管fromAccount和toAccount的两个参数怎么调换,都能保证ThreadA和ThreadB获取锁的顺序是一致的,不会造成死锁问题。
下面在处理下fromAccount.hashCode和toAccount.hashCode相等的情况,这个情况好判断,在加一把锁就可以了:
/**
* 转账方法
* @param fromAccount 谁转账
* @param toAccount 转账给谁
* @param amount 转账金额
*/
private static final Object lock = new Object();
public void transferMoneyMoney(Account fromAccount,Account toAccount,DollarAmount amount) {
if(fromHash>toHash) {
}else if(fromHash
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?