【问题标题】:Assuring wait() is called before notify() in threading, is it possible?确保在线程中的 notify() 之前调用 wait(),这可能吗?
【发布时间】:2013-04-12 19:36:35
【问题描述】:

当我遇到一个使用等待/通知方法的示例时,我正在查看 Kathy Sierra 书中的线程一章:

 class ThreadA {
 public static void main(String [] args) {
 ThreadB b = new ThreadB();
 b.start();

 synchronized(b) {
 try {
    System.out.println("Waiting for b to complete...");
    b.wait();
     } catch (InterruptedException e) {}
 System.out.println("Total is: " + b.total);
 }
}
}

class ThreadB extends Thread { 
 int total;
 public void run() {
  synchronized(this) {
   for(int i=0;i<100;i++) {
   total += i;
   }
  notify();
 }
}
}

运行代码总是产生相同的输出:

等待 b 完成...总计:4950

我在 ThreadB 中修改了 run() 的同步块,添加了:

System.out.println("ThreadB is executed"); 

问题是:为什么我一直得到

“正在等待 b 完成...”

之前

“线程B被执行”

?是不是有可能线程b先于主线程执行?

【问题讨论】:

  • 这是一本书的逐字代码?它有一个竞争条件......
  • 这段代码在很多层面上都很愚蠢,很难知道从哪里开始。这真的是出自出版书籍吗?令人兴奋。
  • 没有竞争条件---b == this。但它几乎无法挽救这一天。
  • 关于相同示例代码的问题:java and synchronization
  • @TedHopp 我不同意。比赛危险仅仅是程序的结果取决于不可预测的事件的情况。在这种情况下,我们无法保证执行顺序,因此;种族危险。

标签: java thread-safety race-condition


【解决方案1】:

是不是有可能线程 b 在 main 之前执行 线程?

是的,绝对的。

通常wait 带有一些谓词来防止此类问题。

例如,ThreadB 可以有一个表示它已完成的变量。在您的情况下,您可以检查总计是否不为 0。

synchronized (b) {
    try {
        System.out.println("Waiting for b to complete...");
        while (b.total == 0) {
            b.wait();
        }
    } catch (InterruptedException e) {
    }
    System.out.println("Total is: " + b.total);
}

这会在总读取相对于 ThreadB 中的写入创建之前发生的关系。

【讨论】:

  • 上面的代码是一个可怕的线程示例。 +1 指出这一点。
  • @John Vint 有什么解释这些等待伴随谓词是如何实现的吗?
  • @a.u.r 使用wait(或await对应)的开发人员应该自己实现它。从我的示例中得出的重要结论是,b.total 是在同步 b 时写入的,并在相同同步下读取。 Javadoc 以类似的方式解释 docs.oracle.com/javase/1.5.0/docs/api/java/lang/…
  • @a.u.r 在我看来,你读的书不遵循这个并发习语是不负责任的。
【解决方案2】:

难道不存在线程b先于主线程执行的可能吗?

是的,这种可能性是存在的,尽管对于像这段代码这样的狭隘种族来说,这种可能性很小。

代码在获取 b 的监视器(同步块)之前调用b.start()。主线程有可能在该窗口期间被抢占,线程 B 将首先运行并获取该监视器。

在那种情况下,这个程序会挂起,因为主线程会永远wait(),因为它错过了来自线程 B 的notify()

【讨论】:

  • +1 但还要注意,一个垂死的线程会在其自身上调用notifyAll——这就是join 的实现方式。
  • @MarkoTopolnik 很有趣,不知道,但这并不意味着会通知主线程(B 的run() 可能会返回,并且可能会在 A 之前发送“死亡通知()”正在等待 - 我同意这是极不可能的,但仍然)。
  • 对你的主要观点没有争议(因此 +1),但很高兴知道:StackOverflow 已经看到了很多问题,其中提到 OP 的代码,notify 完全缺失,但是代码仍然可以神奇地工作。
猜你喜欢
  • 2012-05-10
  • 1970-01-01
  • 1970-01-01
  • 2013-11-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-30
相关资源
最近更新 更多