产生这种情况的原因,是不同的线程通过不同顺序去获取相同的锁;比如线程1获取锁的顺序是left -> right,而线程2获取锁的顺序是right -> left,在某种情况下会发生死锁。拿上面的案例分析,我们通过Java自带的jps和jstack工具查看java进程ID和线程相关信息。
jps查看LeftRightDeadLock的进程id为17968
jstack查看进程中的线程信息,线程信息比较多,我把重要的复制出来,如下的图中能很明显的看到产生了死锁。
这里省略了很多线程当前状态信息
解决顺序死锁的办法其实就是保证所有线程以相同的顺序获取锁就行。
3.2 动态锁顺序死锁
3.2.1 动态锁顺序死锁的产生与示例
动态锁顺序死锁与上面的锁顺序死锁其实最本质的区别,就在于动态锁顺序死锁锁住的资源无法确定或者会发生改变。
比如说银行转账业务中,账户A向账户B转账,账户B也可以向账户A转账,这种情况下如果加锁的方式不正确就会发生死锁,比如如下代码:
定义简单的账户类Account
package com.liziba.dl;
import java.math.BigDecimal;
/**
-
-
账户类
-
@Author: Liziba
*/
public class Account {
/** 账户 */
public String number;
/** 余额 */
public BigDecimal balance;
public Account(String number, BigDecimal balance) {
this.number = number;
this.balance = balance;
}
public void setNumber(String number) {
this.number = number;
}
public void setBalance(BigDecimal balance) {
this.balance = balance;
}
}
定义转账类TransferMoney,其中有transferMoney()方法用于accountFrom账户向accountTo转账金额amt:
package com.liziba.dl;
import java.math.BigDecimal;
/**
-
-
转账类
-
@Author: Liziba
*/
public class TransferMoney {
/**
-
转账方法
-
@param accountFrom 转账方
-
@param accountTo 接收方
-
@param amt 转账金额
-
@throws Exception
*/
public static void transferMoney(Account accountFrom,
Account accountTo,
BigDecimal amt) throws Exception {
synchronized (accountFrom) {
synchronized (accountTo) {
BigDecimal formBalance = accountFrom.balance;
if (formBalance.compareTo(amt) < 0) {
throw new Exception(accountFrom.number + " balance is not enough.");
} else {
accountFrom.setBalance(formBalance.subtract(amt));
accountTo.se