美文网首页
通过锁顺序来避免动态的锁顺序死锁

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

作者: 阿祥小王子 | 来源:发表于2018-11-16 20:58 被阅读0次

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

    欢迎关注作者简书
    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猿社区

    欢迎加入Java猿社区.png

    相关文章

      网友评论

          本文标题:通过锁顺序来避免动态的锁顺序死锁

          本文链接:https://www.haomeiwen.com/subject/rkjvfqtx.html