我正在查看一些通知/等待示例并遇到了这个问题。我理解同步块本质上定义了一个关键部分,但是这不是一个竞争条件吗? Nothing指定首先输入哪个同步块。
public 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){
e.printStackTrace();
}
System.out.println("Total is: " + b.total);
}
}
}
class ThreadB extends Thread {
int total;
@Override
public void run(){
synchronized(this){
for(int i=0; i<100 ; i++){
total += i;
}
notify();
}
}
}
每个网站的输出:
等待b完成......
总计:4950
答案 0 :(得分:5)
是的,它不能保证首先执行哪个线程。线程b可以在主线程开始等待之前进行通知。
除此之外,线程可以在没有得到通知的情况下从等待返回,因此在进入等待之前设置标志并检查它在技术上是不够的。您可以将其重写为类似
的内容public class ThreadA {
public static void main(String[] args) throws InterruptedException {
ThreadB b = new ThreadB();
b.start();
synchronized(b){
while (!b.isDone()) {
System.out.println("Waiting for b to complete...");
b.wait();
}
System.out.println("Total is: " + b.total);
}
}
}
class ThreadB extends Thread {
int total;
private boolean done = false;
@Override
public void run(){
synchronized(this){
for(int i=0; i<100 ; i++){
total += i;
}
done = true;
notify();
}
}
public boolean isDone() {return done;}
}
这样主线程将等到b完成计算,无论谁先开始。
顺便说一句,API文档建议您不要在线程上进行同步。 JDK在线程上同步以实现Thread#join。终止的线程发送notifyAll,它接收到的任何东西都会收到。如果您从已获得锁定的线程中调用notify或notifyAll,则可以提前返回。这里的一个副作用是,如果你删除通知代码以相同的方式工作。
答案 1 :(得分:2)
是的,这是竞争条件。没有什么能阻止ThreadB启动,进入其run方法,并在ThreadA进入同步块之前自行同步(从而无限期地等待)。但是,考虑到新线程开始执行所需的时间,它不太可能发生。
处理此类情况的最简单,最推荐的方法是不编写自己的实现,而是选择使用Executor提供的可调用/未来。
要在不遵循标准的情况下修复此特定情况:
答案 2 :(得分:2)
是 - 这是一个关于哪个线程首先进入哪个同步块的竞赛。对于比赛的大多数情况,输出和答案将是相同的。但是,对于其中一个,该程序将陷入僵局:
在这种情况下,main将永远等待,因为线程b在wait()之前被阻塞时调用notify。
尽管如此,这是不太可能的 - 但是通过所有线程,你应该得出结论,它会发生,然后是在最糟糕的时候。