通过锁顺序来避免动态的锁顺序死锁
欢迎关注作者简书
csdn传送门
前言
两个线程试图通过不同的顺序获取多个相同的锁。如果请求的顺序不相同,那么会出现循环的锁依赖现象,产生死锁。但是如果保证同时请求锁L和锁M的每一个线程,都是按照从 L 到 M 的顺序,那么就不会发生死锁了。
比如:银行账户转账问题,两个用户转账的话,如果采用一般的synchronized嵌套的话,容易造成死锁。
思想
我们可以制定锁的顺序,并在整个应用程序中,获得锁都必须始终遵守这个既定的顺序。我们在制定对象顺序的时候,可以使用System.identityHashCode这样一种方式,它会返回Object.hashcode所返回的值。 在极少数的情况下,2个对象具有相同的哈希码,我们必须使用任意的中数来决定锁的顺序,这又重新引入了死锁的可能性。这个时候我们使用另一个锁(加时赛锁),在获得2个对象的锁之前,就要获得这个锁。
/**
* @program:
* @description: 动态的锁顺序死锁的解决方案——通过锁顺序来避免死锁
* @author: zhouzhixiang
* @create: 2018-11-16 20:03
*/
public class ThreadTest5 {
// 加时赛锁
private static final Object tieLock = new Object();
class Account implements Comparable{
String username;
String password;
long moneycount;
void debit(long amount) {
moneycount = moneycount - amount;
}
void credit(long amount) {
moneycount = moneycount + amount;
}
@Override
public int compareTo(Object o) {
return (int) (moneycount - ((long)o));
}
}
/**
* 此方法容易发生动态的锁顺序死锁——错误方式
* @param fromAccount 转账方
* @param toAccount 收账方
* @param amount 转账金额
*/
public void tranferMoney(Account fromAccount, Account toAccount, long amount) throws InsufficientResourcesException {
synchronized(fromAccount) {
synchronized (toAccount) {
if(fromAccount.compareTo(amount) < 0)
throw new InsufficientResourcesException();
else {
fromAccount.debit(amount);
toAccount.credit(amount);
}
}
}
}
/**
* 通过锁顺序来避免死锁——正确方法
* @param fromAccount 转账方
* @param toAccount 收账方
* @param amount 转账金额
*/
public void tranferMoney2(Account fromAccount, Account toAccount, long amount) throws InsufficientResourcesException {
class Helper {
public void tranfer() throws InsufficientResourcesException {
if(fromAccount.compareTo(amount) < 0)
throw new InsufficientResourcesException();
else {
fromAccount.debit(amount);
toAccount.credit(amount);
}
}
}
int fromHashCode = System.identityHashCode(fromAccount);
int toHashCode = System.identityHashCode(toAccount);
if(fromHashCode < toHashCode) {
synchronized (fromAccount) {
synchronized (toAccount) {
new Helper().tranfer();
}
}
}else if (fromHashCode > toHashCode) {
synchronized (toAccount) {
synchronized (fromAccount) {
new Helper().tranfer();
}
}
}else {
// 加时赛锁
synchronized (tieLock) {
synchronized (fromAccount) {
synchronized (toAccount) {
new Helper().tranfer();
}
}
}
}
}
}
欢迎加入Java猿社区
网友评论