zoukankan      html  css  js  c++  java
  • JAVA并发编程学习笔记------协作对象之间发生的死锁

    一、 如果在持有锁时调用某个外部方法,那么将出现活跃性问题。在这个外部方法中可能会获取其他锁(这可能会产生死锁),或者阻塞时间过长,导致其他线程无法及时获得当前被持有的锁。如下代码:

    public class Taxi {
        private final Dispatcher dispatcher;
        private Point location, destination;
    
        public Taxi(Dispatcher dispatcher) {
            this.dispatcher = dispatcher;
        }
    
        public synchronized Point getLocation() {
            return location;
        }
    
        public synchronized void setLocation(Point location){
            this.location = location;
            if(location.equals(destination)){
                dispatcher.notifyAvaliable(this);
            }
        }
    }
    
    public class Dispatcher {
        private final Set<Taxi> taxis;
        private final Set<Taxi> avaliableTaxis;
    
        public Dispatcher() {
            taxis = new HashSet<Taxi>();
            avaliableTaxis = new HashSet<Taxi>();
        }
    
        public synchronized void notifyAvaliable(Taxi taxi) {
            avaliableTaxis.add(taxi);
        }
    
        public synchronized Image getImage() {
            Image image = new Image();
            for (Taxi t : taxis) {
                image.drawMarker(t.getLocation());
            }
            return image;
        }
    }
    

      尽管没有任何方法会显式的获取两个锁,但setLocation和getImage等方法的调用者都会获得两个锁。因为setLocation和notifyAvailable都是同步方法,因此调用setLocation的线程将首先获得Taxi的锁,然后获取Dispatcher的锁,同样调用getImage的线程将首先获取Dispatcher的锁,然后再获取每一个Taxi的锁,两个线程按照不同的顺序来获取两个锁,这时就有可能产生死锁。

      解决方案是开放调用(如果在调用某个方法时不需要持有锁,那么这种调用被称为开放调用),使同步代码块仅被用于保护那些涉及共享状态的操作,修改代码如下:

    public class Taxi {
        private final Dispatcher dispatcher;
        private Point location, destination;
    
        public Taxi(Dispatcher dispatcher) {
            this.dispatcher = dispatcher;
        }
    
        public synchronized Point getLocation() {
            return location;
        }
    
        public synchronized void setLocation(Point location) {
            boolean reachedLocation;
            synchronized (this) {
                this.location = location;
                reachedLocation = location.equals(destination);
            }
            if (reachedLocation) {
                dispatcher.notifyAvaliable(this);
            }
        }
    }
    
    public class Dispatcher {
        private final Set<Taxi> taxis;
        private final Set<Taxi> avaliableTaxis;
    
        public Dispatcher() {
            taxis = new HashSet<Taxi>();
            avaliableTaxis = new HashSet<Taxi>();
        }
    
        public synchronized void notifyAvaliable(Taxi taxi) {
            avaliableTaxis.add(taxi);
        }
    
        public Image getImage(){
            Set<Taxi> copy;
            synchronized (this){
                copy = new HashSet<Taxi>();
            }
            Image image = new Image();
            for(Taxi t: copy){
                image.drawMarker(t.getLocation());
            }
            return image;
        }
    }
    

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

    二、除死锁外,还有几种活跃性危险,简单了解下:

    (1)饥饿:当线程由于无法访问它所需要的资源而不能继续执行时,就发生了“饥饿”。
          引发饥饿的最常见资源就是CPU始终周期。
    (2)活锁:线程将不断重复执行某个消息,而且总会失败。
          活锁通常发生在处理事务消息的应用程序中:如果不能成功的处理某个消息,那么消息处理机制将回滚整个事务,并将它重新放到队列的开头。
          当多个相互协作的线程都对彼此进行响应从而修改各自的状态,并使得任何一个线程否无法继续执行时,就发生了活锁。
          解决活锁的方案是在重试机制中引入随机性。

    三、 减少锁的竞争:

    1、有两个因素会影响在锁上发生竞争的可能性: 锁的请求频率,以及每次持有该锁的时间。

    2、有三种方式可以降低锁的竞争程度:
      1)减少锁的持有时间:实际情况中,仅当可以将一些“大量”的计算或阻塞操作从同步代码块中移出时,才应该考虑同步代码块的大小。
      2)降低锁的请求频率;
      3)使用带有协调机制的独占锁,这些机制允许更高的并发性。

  • 相关阅读:
    HDU 6182 A Math Problem 水题
    HDU 6186 CS Course 位运算 思维
    HDU 6188 Duizi and Shunzi 贪心 思维
    HDU 2824 The Euler function 欧拉函数
    HDU 3037 Saving Beans 多重集合的结合 lucas定理
    HDU 3923 Invoker Polya定理
    FZU 2282 Wand 组合数学 错排公式
    HDU 1452 Happy 2004 数论
    HDU 5778 abs 数论
    欧拉回路【判断连通+度数为偶】
  • 原文地址:https://www.cnblogs.com/hunterCecil/p/6196237.html
Copyright © 2011-2022 走看看