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)使用带有协调机制的独占锁,这些机制允许更高的并发性。

  • 相关阅读:
    python设计模式之命令模式
    [Shell] 生成日期列表
    [Python] Python 获取文件路径
    [Search] 倒排索引与bool检索
    [NLP] Reformer: The Efficient Transformer
    [Alg] 随机抽样完备算法-蓄水池算法 Reservoir Sampling
    [Chaos] 混沌数学学习资料
    [Alg] 文本匹配-多模匹配-WM算法
    [Alg]文本匹配-单模匹配-Sunday算法
    [Alg] 文本匹配-多模匹配-AC自动机
  • 原文地址:https://www.cnblogs.com/hunterCecil/p/6196237.html
Copyright © 2011-2022 走看看