`
2277259257
  • 浏览: 515322 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Slipped Conditions

 
阅读更多

Slipped Conditions

所谓 Slipped conditions,就是说, 从一个线程检查某一特定条件到该线程操作此条件期间,这个条件已经被其它线程改变,导致第一个线程在该条件上执行了错误的操作。这里有一个简单的例子:

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();
    }
}

我们可以看到,lock()方法包含了两个同步块。第一个同步块执行 wait 操作直到 isLocked 变为 false 才退出,第二个同步块将 isLocked 置为 true,以此来锁住这个 Lock 实例避免其它线程通过 lock()方法。

我们可以设想一下,假如在某个时刻 isLocked 为 false, 这个时候,有两个线程同时访问 lock 方法。如果第一个线程先进入第一个同步块,这个时候它会发现 isLocked 为 false,若此时允许第二个线程执行,它也进入第一个同步块,同样发现 isLocked 是 false。现在两个线程都检查了这个条件为 false,然后它们都会继续进入第二个同步块中并设置 isLocked 为 true。

这个场景就是 slipped conditions 的例子,两个线程检查同一个条件, 然后退出同步块,因此在这两个线程改变条件之前,就允许其它线程来检查这个条件。换句话说,条件被某个线程检查到该条件被此线程改变期间,这个条件已经被其它线程改变过了。

为避免 slipped conditions,条件的检查与设置必须是原子的,也就是说,在第一个线程检查和设置条件期间,不会有其它线程检查这个条件。

解决上面问题的方法很简单,只是简单的把 isLocked = true 这行代码移到第一个同步块中,放在 while 循环后面即可:

public class Lock {
    private boolean isLocked = true;

    public void lock(){
      synchronized(this){
        while(isLocked){
          try{
            this.wait();
          } catch(InterruptedException e){
            //do nothing, keep waiting
          }
        }
        isLocked = true;
      }
    }

    public synchronized void unlock(){
      isLocked = false;
      this.notify();
    }
}

现在检查和设置 isLocked 条件是在同一个同步块中原子地执行了。

一个更现实的例子

也许你会说,我才不可能写这么挫的代码,还觉得 slipped conditions 是个相当理论的问题。但是第一个简单的例子只是用来更好的展示 slipped conditions。

饥饿和公平中实现的公平锁也许是个更现实的例子。再看下嵌套管程锁死中那个幼稚的实现,如果我们试图解决其中的嵌套管程锁死问题,很容易产生 slipped conditions 问题。 首先让我们看下嵌套管程锁死中的例子:

//Fair Lock implementation with nested monitor lockout problem
public class FairLock {
  private boolean isLocked = false;
  private Thread lockingThread = null;
  private List waitingThreads =
            new ArrayList();

  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();
      }
    }
  }
}
public class QueueObject {}

我们可以看到 synchronized(queueObject)及其中的 queueObject.wait()调用是嵌在 synchronized(this)块里面的,这会导致嵌套管程锁死问题。为避免这个问题,我们必须将 synchronized(queueObject)块移出 synchronized(this)块。移出来之后的代码可能是这样的:

//Fair Lock implementation with slipped conditions problem
public class FairLock {
  private boolean isLocked = false;
  private Thread lockingThread  = null;
  private List waitingThreads =
            new ArrayList();

  public void lock() throws InterruptedException{
    QueueObject queueObject = new QueueObject();

    synchronized(this){
      waitingThreads.add(queueObject);
    }

    boolean mustWait = true;
    while(mustWait){

      synchronized(this){
        mustWait = isLocked || waitingThreads.get(0) != queueObject;
      }

      synchronized(queueObject){
        if(mustWait){
          try{
            queueObject.wait();
          }catch(InterruptedException e){
            waitingThreads.remove(queueObject);
            throw e;
          }
        }
      }
    }

    synchronized(this){
      waitingThreads.remove(queueObject);
      isLocked = true;
      lockingThread = Thread.currentThread();
    }
  }
}

注意:因为我只改动了 lock()方法,这里只展现了 lock 方法。

现在 lock()方法包含了 3 个同步块。

第一个,synchronized(this)块通过 mustWait = isLocked || waitingThreads.get(0) != queueObject 检查内部变量的值。

第二个,synchronized(queueObject)块检查线程是否需要等待。也有可能其它线程在这个时候已经解锁了,但我们暂时不考虑这个问题。我们就假设这个锁处在解锁状态,所以线程会立马退出 synchronized(queueObject)块。

第三个,synchronized(this)块只会在 mustWait 为 false 的时候执行。它将 isLocked 重新设回 true,然后离开 lock()方法。

设想一下,在锁处于解锁状态时,如果有两个线程同时调用 lock()方法会发生什么。首先,线程 1 会检查到 isLocked 为 false,然后线程 2 同样检查到 isLocked 为 false。接着,它们都不会等待,都会去设置 isLocked 为 true。这就是 slipped conditions 的一个最好的例子。

解决 Slipped Conditions 问题

要解决上面例子中的 slipped conditions 问题,最后一个 synchronized(this)块中的代码必须向上移到第一个同步块中。为适应这种变动,代码需要做点小改动。下面是改动过的代码:

//Fair Lock implementation without nested monitor lockout problem,
//but with missed signals problem.
public class FairLock {
  private boolean isLocked = false;
  private Thread lockingThread  = null;
  private List waitingThreads =
            new ArrayList();

  public void lock() throws InterruptedException{
    QueueObject queueObject = new QueueObject();

    synchronized(this){
      waitingThreads.add(queueObject);
    }

    boolean mustWait = true;
    while(mustWait){
      synchronized(this){
        mustWait = isLocked || waitingThreads.get(0) != queueObject;
        if(!mustWait){
          waitingThreads.remove(queueObject);
          isLocked = true;
          lockingThread = Thread.currentThread();
          return;
        }
      }     

      synchronized(queueObject){
        if(mustWait){
          try{
            queueObject.wait();
          }catch(InterruptedException e){
            waitingThreads.remove(queueObject);
            throw e;
          }
        }
      }
    }
  }
}

我们可以看到对局部变量 mustWait 的检查与赋值是在同一个同步块中完成的。还可以看到,即使在 synchronized(this)块外面检查了 mustWait,在 while(mustWait)子句中,mustWait 变量从来没有在 synchronized(this)同步块外被赋值。当一个线程检查到 mustWait 是 false 的时候,它将自动设置内部的条件(isLocked),所以其它线程再来检查这个条件的时候,它们就会发现这个条件的值现在为 true 了。

synchronized(this)块中的 return;语句不是必须的。这只是个小小的优化。如果一个线程肯定不会等待(即 mustWait 为 false),那么就没必要让它进入到 synchronized(queueObject)同步块中和执行 if(mustWait)子句了。

细心的读者可能会注意到上面的公平锁实现仍然有可能丢失信号。设想一下,当该 FairLock 实例处于锁定状态时,有个线程来调用 lock()方法。执行完第一个 synchronized(this)块后,mustWait 变量的值为 true。再设想一下调用 lock()的线程是通过抢占式的,拥有锁的那个线程那个线程此时调用了 unlock()方法,但是看下之前的 unlock()的实现你会发现,它调用了 queueObject.notify()。但是,因为 lock()中的线程还没有来得及调用 queueObject.wait(),所以 queueObject.notify()调用也就没有作用了,信号就丢失掉了。如果调用 lock()的线程在另一个线程调用 queueObject.notify()之后调用 queueObject.wait(),这个线程会一直阻塞到其它线程调用 unlock 方法为止,但这永远也不会发生。

公平锁实现的信号丢失问题在饥饿和公平一文中我们已有过讨论,把 QueueObject 转变成一个信号量,并提供两个方法:doWait()和 doNotify()。这些方法会在 QueueObject 内部对信号进行存储和响应。用这种方式,即使 doNotify()在 doWait()之前调用,信号也不会丢失

分享到:
评论

相关推荐

    英语四级单词690个

    - 例句:He slipped on the wet floor and fell down.(他在湿滑的地面上滑了一跤。) 10. **slide** (v.) 滑动,滑落 (n.) 滑动;滑面;幻灯片 - 例句:The children were sliding down the grassy hill.(孩子...

    新四级700核心词汇(09年最新版)

    - *例句*: He slipped on the ice and fell down. (他在冰上滑了一跤,摔倒了。) 10. **slide v. 滑动;n. 幻灯片;滑动** - *含义*: 物体沿表面移动,通常不受控制。 - *例句*: The book slid off the table. ...

    英语六级词汇汇总——百词斩单词汇总.docx

    - He *slipped* on the wet floor and hurt his ankle. - A *slip* of the tongue can sometimes lead to misunderstandings. #### slippery 滑的、易滑倒的 - **定义与用法**:形容物体表面光滑,容易使人或物...

    2012年英语四级核心词汇

    - **例句**:The company decided to alter its business strategy in response to the changing market conditions. - **解析**:alter通常用于指对原有计划、设计或状态做出修改,强调的是“改变”这一动作。 ###...

    大学英语四级700高频词汇(带音标修正版).doc

    如:The weather conditions were extreme, making it difficult to travel.(天气状况极为恶劣,难以出行。) 41. agent: 名词,指“代理人,动因,原因”。如:A travel agent can help plan your vacation....

    (免费)考生必看:大学英语四级690个高频词汇

    - **例句**:The company decided to alter its marketing strategy in response to the changing market conditions. ##### 2. burst - **词性**:动词/名词 - **含义**:突然发生、爆裂 - **例句**:There was a ...

    08年四级高频词汇详解

    - **例句**:They faced extreme weather conditions during the expedition. 以上列举了一些四级高频词汇的含义及例句,通过学习这些词汇可以有效提高英语水平,为英语四六级考试打下坚实的基础。

    新四级冲刺需牢记的700核心词

    - 示例:The weather conditions were extreme, with temperatures dropping below zero. 以上词汇仅为新四级冲刺所需牢记的700核心词中的一部分,通过深入理解每个单词的意义及其应用场景,可以更好地掌握这些...

    大学英语四级690个高频词汇

    - **例句**:Weather conditions vary greatly in different parts of the world.(世界各地的天气状况差异很大。) #### 23. vanish vi. 消灭,不见 - **定义与用法**:指某物突然消失得无影无踪。 - **例句**:...

    2012新四级700高频词汇

    - **例句**:She slipped on the ice and fell down. 10. **slide** [slaɪd] - **含义**:v. 滑动,滑落 n. 滑动;滑面;幻灯片 - **例句**:The child slid down the slide at the playground. 11. **...

    英语四级经常出现的词汇

    例如:“He slipped on the ice and fell down.”(他在冰上滑倒了。) ### slide (v., n.) 作动词时意为滑动、滑行;作名词时,意为幻灯片或滑梯。例如:“She slid the book across the table.”(她把书沿着桌子...

    大学英语四级重点词汇700词

    例如:“I slipped on the ice and fell down.”(我在冰上滑倒了。) #### slide v. 滑动,滑落 n. 滑动;滑面;幻灯片 - **解析**:slide 作为动词表示滑动、滑落的动作,也可以作为名词指代幻灯片等。例如:...

    英语四级710分制核心700词汇

    - **例句**:The weather conditions were extreme, making it difficult to travel. #### 41. agent n. 代理人,代理商;动因,原因 - **释义**:作为名词时,可以指代表他人行事的人,也可以指导致变化的因素。 -...

    大学英语四级高频词汇

    - **例句**: *He slipped on the ice and fell.* (他在冰上滑了一跤。) 10. **slide** (v./n.) 滑动,滑落 / 滑面;幻灯片 - **例句**: *The children were sliding down the snowy hill.* (孩子们在雪山上滑行。...

Global site tag (gtag.js) - Google Analytics