Java 死锁

线程死锁操作的一般情况都是,等待拿到某一个锁来进行操作或者说某一个资源,如果一直拿不到的话,那么就一直阻塞,导致程序无法正常结束或者终止.

有一个非常经典的问题可以说明这个现象(哲学家吃饭问题),5个哲学家去吃饭,坐在一张圆桌旁,

他们有5根筷子,并且每两个人中间放一根筷子,哲学家们时而思考,时而进餐,每个人都需要一双筷子才能吃到东西,并在吃完后将筷子放回原处继续思考。

一般情况下,每个人都迅速的拿到自己左边的筷子,然后尝试拿右边的筷子,但是同时不放下自己手上的筷子,而是等待其他人放下他的筷子,这就产生了死锁。 有一些管理筷子的算法,能够使每个人都吃到东西。比如 我拿到了筷子之后,尝试拿另外一根筷子时发现其他人已经拿走了,我就放弃自己手中的筷子,并且过段时间后在去尝试。

这样的做法在性能上有一定的损耗。我们先来看下面这段程序,银行帐户之间转账。

 1 public static void transferMoney(final AccountUser a1 , final AccountUser a2, final double money) {
 2         synchronized (a1) {
 3             synchronized (a2) {
 4                 // do something.
 5                 if (a2.getBalance()  -  money > 0) {
 6                     a2.setBalance(a2.getBalance()  -  money);
 7                     a1.setBalance(a1.getBalance()  +  money);
 8                 } else {
 9                     throw new RuntimeException("Error");
10                 }
11             }
12         }
13 }

假设 现在有这样一个情况,A用户向B用户转账,同时B用户向A用户转账.

这种加锁的顺序,很可能会导致死锁。这种情况称为Lock-Ordering Deadlock(锁顺序死锁). 线程1 先拿到A用户的锁,线程2 拿到B用户锁,线程1等着拿B用户的锁,线程2等着拿A用户的锁,他们如果不进行协调的话,那么他们就会一直等着,导致转账无法正常完成。

我们可以通过控制拿锁的顺序来避免这种情况,将程序修改成下面这种方式

public static void transferMoney(final AccountUser a1 , final AccountUser a2, final double money) {
        int fromHash = System.identityHashCode(a1);
        int toHash = System.identityHashCode(a2); 
        if (fromHash > toHash) {
            synchronized (a1) {
                synchronized (a2) {
                    // do something.
                    if (a2.getBalance()  -  money > 0) {
                        a2.setBalance(a2.getBalance()  -  money);
                        a1.setBalance(a1.getBalance()  +  money);
                    } else {
                        throw new RuntimeException("Error");
                    }
                }
            }
        } else if (fromHash < toHash) {
            synchronized (a2) {
                synchronized (a1) {
                    // do something.
                    if (a2.getBalance()  -  money > 0) {
                        a2.setBalance(a2.getBalance()  -  money);
                        a1.setBalance(a1.getBalance()  +  money);
                    } else {
                        throw new RuntimeException("Error");
                    }
                }
            }
        } else {
            synchronized (lock) {
                synchronized (a1) {
                    synchronized (a2) {
                        // do something.
                        if (a2.getBalance()  -  money > 0) {
                            a2.setBalance(a2.getBalance()  -  money);
                            a1.setBalance(a1.getBalance()  +  money);
                        } else {
                            throw new RuntimeException("Error");
                        }
                    }
                }
            }
        }
    }

通过hashCode的值来简单的判断,从而避免死锁发生的可能性。如果出现A转账给A的情况,hashCode一样的话,那么我们就在加一个额外的锁来控制他。

Java 死锁,古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。