zoukankan      html  css  js  c++  java
  • 第10章避免活跃性危险

    如果每个需要锁L和锁M的线程都以相同的顺序来获取L和M,那么就不会发生死锁了。

    解决这个问题,必须定义锁的顺序,并在整个应用程序中都按照这个顺序来获取锁。

    在制定锁的顺序时,可以使用System.identityHashCode方法,该方法将返回由Object.hashCode返回的值。

        private static final Object tieLock = new Object();
        
        public void transferMoney(final Account fromAcct,
                                  final Account toAcct,
                                  final DollarAmount amount) 
            throws InsufficientFundsException {
            class Helper {
                public void transfer() throws InsufficientFundsException {
                    if (fromAcct.getBalance().compareTo(amount) < 0) {
                        throw new InsufficientFundsException();
                    } else {
                        fromAcct.debit(amount);
                        toAcct.credit(amount);
                    }
                }
            }
            int fromHash = System.identityHashCode(fromAcct);
            int toHash = System.identityHashCode(toAcct);
            
            if (fromHash < toHash) {
                synchronized (fromAcct) {
                    synchronized (toAcct) {
                        new Helper().transfer();
                    }
                }
            }
            else if (fromHash > toHash) {
                synchronized (toAcct) {
                    synchronized (fromAcct) {
                        new Helper().transfer();
                    }
                }
            }
            else {
                synchronized (tieLock) {
                    synchronized (fromAcct) {
                        synchronized (toAcct) {
                            new Helper().transfer();
                        }
                    }
                }
            }
        }

    在极少数情况下,两个对象可能拥有相同的散列值,此时必须通过某种任意的方法来决定锁的顺序,而这可能又会重新引入死锁。为了避免这种情况,可以使用“加时赛(TieBreaking)”锁。在获得两个Account锁之前,首先获得这个“加时赛”锁,从而保证每次只有一个线程以未知的顺序获得这两个锁,从而消除了死锁发生的可能性(只要一致地使用这种机制)。如果经常会出现散列冲突的情况,那么这种技术可能会成为并发性的一个瓶颈(这类似于在整个程序中只有一个锁的情况),但由于System.identityHashCode中出现散列冲突的频率非常低,因此这项技术以最小的代价,换来了最大的安全性。

    如果在调用某个方法时不需要持有锁,那么这种调用被称为开放调用(Open Call)。

    在程序中尽量使用开放调用。与那些在持有锁时调用外部方法的程序相比,更易于对依赖开放调用的程序进行死锁分析。

    要避免使用线程优先级,因为这会增加平台依赖性,并可能导致活跃性问题。在大多数并发应用程序中,都可以使用默认的线程优先级。

  • 相关阅读:
    线程阶段性总结——APM,ThreadPool,Task,TaskScheduler ,CancellationTokenSource
    研究BackgroundWorker后发现:AsyncOperation和SynchronizationContext的差异真的很大!
    线程同步——优势、劣势
    APM异步编程模型的优势
    DataGridView的VirtualMode,在大容量数据加载时特别有用
    【C】——C语言的位运算的优势
    【linux】——Linux tar打包命令
    【C】用文件和链表实现学生信息管理
    【C】——回调函数的好处
    【C】strcpy()需谨慎使用;
  • 原文地址:https://www.cnblogs.com/a0000/p/4903364.html
Copyright © 2011-2022 走看看