美文网首页
7. 嵌套管程锁死&Slipped Condition

7. 嵌套管程锁死&Slipped Condition

作者: 进击的勇士 | 来源:发表于2017-03-28 18:28 被阅读0次

    场景介绍

    public class Lock{
      protected MonitorObject monitorObject = new MonitorObject();
      protected boolean isLocked = false;
    
      public void lock() throws InterruptedException{
        synchronized(this){
          while(isLocked){
            synchronized(this.monitorObject){
                this.monitorObject.wait();
            }
          }
          isLocked = true;
        }
      }
    
      public void unlock(){
        synchronized(this){
          this.isLocked = false;
          synchronized(this.monitorObject){
            this.monitorObject.notify();
          }
        }
      }
    }
    
    1. lock()方法在this上同步,然后在monitorObject上同步。如果isLocked为true,调用lock()方法的线程会在wait()方法上阻塞。
    2. 如果此时需要unlock,由于wait()只释放了monitorObject的锁,this相关的管程对象并没有释放,所以unlock()方法等待lock()方法释放锁,而lock()方法必须等待unlock()方法的isLocked变为false。

    简而言之,在lock方法中等待的线程需要其它线程成功调用unlock方法来退出lock方法,但是,在lock()方法离开外层同步块之前,没有线程能成功执行unlock()。

    另一个例子

    public class FairLock {
      private boolean isLocked = false;
      private Thread lockingThread  = null;
      private List<QueueObject> waitingThreads =
                new ArrayList<QueueObject>();
    
      public void lock() throws InterruptedException{
        QueueObject queueObject = new QueueObject();
    
        synchronized(this){
          waitingThreads.add(queueObject);
    
          while(isLocked || waitingThreads.get(0) != queueObject){
    
            synchronized(queueObject){
              try{
                queueObject.wait();
              }catch(InterruptedException e){
                waitingThreads.remove(queueObject);
                throw e;
              }
            }
          }
          waitingThreads.remove(queueObject);
          isLocked = true;
          lockingThread = Thread.currentThread();
        }
      }
    
      public synchronized void unlock(){
        if(this.lockingThread != Thread.currentThread()){
          throw new IllegalMonitorStateException(
            "Calling thread has not locked this lock");
        }
        isLocked = false;
        lockingThread = null;
        if(waitingThreads.size() > 0){
          QueueObject queueObject = waitingThread.get(0);
          synchronized(queueObject){
            queueObject.notify();
          }
        }
      }
    }
    
    1. 方法内部有两个synchronized块,一个锁定this,一个嵌在上一个synchronized块内部,它锁定的是局部变量queueObject。
    2. 当一个线程调用queueObject.wait()方法的时候,它仅仅释放的是在queueObject对象实例的锁,并没有释放”this”上面的锁。
    3. unlock方法被声明成了synchronized,这就相当于一个synchronized(this)块。这就意味着,如果一个线程在lock()中等待,该线程将持有与this关联的管程对象。所有调用unlock()的线程将会一直保持阻塞,等待着前面那个已经获得this锁的线程释放this锁,但这永远也发生不了,因为只有某个线程成功地给lock()中等待的线程发送了信号,this上的锁才会释放,但只有执行unlock()方法才会发送这个信号。

    嵌套管程锁死VS死锁

    管程:获取锁的顺序是一致的。线程1获取A和B,然后释放B,等待2的信号。线程2需要同时获得A和B,才能向1发送信号。一个线程等待唤醒,另一个线程等待想要的锁被释放。

    死锁:两个线程获取锁的顺序不一致,两个线程都在等待对方释放锁。

    Slipped Condition

    slipped condition是说从一个线程检查一个特定条件到该线程操作此条件期间,这个条件已经被其他线程改变,导致第一个线程在该条件上发生了错误的操作。

    public class Lock {
    
        private boolean isLocked = true;
    
        public void lock(){
          synchronized(this){
            while(isLocked){
              try{
                this.wait();
              } catch(InterruptedException e){
                //do nothing, keep waiting
              }
            }
          }
    
          synchronized(this){
            isLocked = true;
          }
        }
    
        public synchronized void unlock(){
          isLocked = false;
          this.notify();
        }
    
    }
    

    如果线程A先进入第一个lock同步块,这时候发现isLock为false。此时第二个线程执行,也进入第一个同步块,同样发现isLocked是false。此时两个线程都检查了这个条件,然后回继续进入到第二个同步快并设置isLocked为true。

    修复方法:将isLocked = true移到第一个同步块中。

    问题:变量的检查与赋值应该在同一个同步块中完成。

    相关文章

      网友评论

          本文标题:7. 嵌套管程锁死&Slipped Condition

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