通过锁顺序来避免动态的锁顺序死锁

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/danielzhou888/article/details/84145801

通过锁顺序来避免动态的锁顺序死锁

欢迎关注作者博客
简书传送门

前言

  两个线程试图通过不同的顺序获取多个相同的锁。如果请求的顺序不相同,那么会出现循环的锁依赖现象,产生死锁。但是如果保证同时请求锁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猿社区
欢迎加入Java猿社区.png

猜你喜欢

转载自blog.csdn.net/danielzhou888/article/details/84145801