`
85977328
  • 浏览: 1898798 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

java并发(十四)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()之前调用,信号也不会丢失。
分享到:
评论

相关推荐

    基于MFC实现的哲学家进餐问题.7z.zip_drivemiy_slipped2uv_哲学家进餐问题

    在计算机科学和并发编程领域,"哲学家进餐问题"(Dining Philosophers Problem)是一个经典的示例,用来揭示资源竞争和死锁的可能性。这个问题由埃德s伽莫夫首次提出,后来被艾兹格迪杰斯特拉进一步阐述,用以解释多...

    BmpZoomPart_CZoomPart_局部放大_Vc_slipped9wg_

    双线性插值是一种常用的高质量放大方法,它通过四个相邻像素的加权平均来计算新位置的像素值。 3. **定义视口和设备上下文**:在VC++中,我们使用`CDC`(Device Context)类来管理图形设备,如屏幕或打印机。我们...

    lmd.rar_decomposition_lmd_mode decomposition_should5nl_slipped6e

    标题 "lmd.rar_decomposition_lmd_mode decomposition_should5nl_slipped6e" 暗示了我们正在讨论一种名为“LMD”(Lagrange Multiplier Decomposition)的方法,这可能是一个信号处理或数据分析的技术,用于分解复杂...

    code_slipped31r_mitk_MFC_

    打开工程找到下属的mitk这个工程编译。出现两个警告和错误。打开出现warning的文件(mitkPointSetGLMapper2D和mitk:SlicedGeometry3D),Ctrl+A全选,然后在文件菜单:file->advanced save options ,在弹出的选项...

    picojs.zip_js_pico.js人脸画框_picojs_slipped866_人脸识别

    人脸识别,精简,利用框架和几个点识别人脸,快速上手

    基于51单片机的iCAN协议程序.zip_iCAN 协议_ican_noneoy8_slipped7rk_单片机

    基于51单片机的iCAN协议程序,是一个很好的例子

    E-M-S docs

    Got absolutely slammed in an interview [Java]。Also interesting was they were completely disinterested in anything I had made or written, it was entirely basic functionality knowledge that I slipped ...

    英语四级高频词汇加例句`

    英语四级高1.alter v. 改变,改动,变更 I'll have to alter the diagram. I've made a mistake 2.burst vi.,n. 突然发生,爆裂 The police burst through the door 3.dispose vi. 除掉;处置;解决;处理(of) I ...

    英语四级单词690个

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

    大学英语四级选词填空单项训练答案

    大学英语四级考试是中国高等教育自学考试委员会组织的一项标准化英语水平考试,旨在测试非英语专业的大学生是否具备完成基础英语学习和交流的能力。四级考试内容包括听力理解、阅读理解、翻译和写作四个部分。其中,...

    四级必备的 仅仅700词 四级无忧

    例句:He slipped on the icy road and fell. 10. slide v. 滑动,滑落 n. 滑动;滑面;幻灯片:常用于描述平滑的移动或幻灯片展示。 例句:The children enjoyed sliding down the hill on their sleds. 11. ...

    英语核心700词及例句

    【英语核心700词及例句】是针对学习者设计的一个词汇强化工具,特别适合备考新大学英语四级710分制的学生。这个资源包含了700个关键的英语单词,每个单词都配有生动的例句,帮助学习者更好地理解和记忆。 1. Alter...

    have.rar_The Environment_swarm

    This document is primarily aimed at particle swarm optimization (pso) algorithm in matlab application said write m files that have slipped into the environment

    英语四级核心高频词汇表.doc

    英语四级核心高频词汇是备考四级考试的关键,这些词汇涵盖了各个主题和语境,包括日常生活、科学、文化等多个领域。以下是一些重点词汇及其详细解释: 1. alter:改变,改动,变更 - 表示对事物进行修改或调整,如...

    课时提升作业(二十)选修7Unit1.doc

    第七题"It is reported that the 60% discounts on some Beijing-Shanghai flights have now slipped back to 20% one day in advance." 句子中省略了when they are,表示在提前一天预订时,折扣回到了20%。...

    2012年英语四级核心词汇

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

    2019大学英语四级高频词汇(精校版).pdf

    例句: She slipped on the icy road and fell. 10. slide v. 滑动,滑落 n. 滑动;滑面;幻灯片 - 描述平滑移动或演示中的幻灯片。 例句: The children enjoyed sliding down the hill on sleds. 11. bacteria n. ...

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

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

    690个最新英语四级高频词汇

    忽略):指物体或人的滑动,也可表示不经意的错误,如:I slipped on the ice and fell. 10. **slide** (v. 滑动,滑落 n. 滑动;滑面;幻灯片):滑行或展示的幻灯片,如:The child slid down the slide in the ...

    历年四级出现频率较高的英语四级词汇.doc

    这份文档是针对英语四级考试中出现频率较高的词汇进行的整理,涵盖了多个领域的词汇,对于备考四级的考生来说是非常有价值的参考资料。以下将详细解释其中的一些重要词汇及其应用: 1. alter:改变,改动。例如:...

Global site tag (gtag.js) - Google Analytics