这个例子中有竞争条件吗?如果是这样,如何避免?
Is there a race condition in this example? If so, how could it be avoided?
我正在查看一些 notify/wait 示例并发现了这个示例。我知道同步块本质上定义了一个关键部分,但这不是竞争条件吗?没有指定首先进入哪个同步块。
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();
}
}
}
每个网站的输出:
Waiting for b to complete...
Total is: 4950
是的,这是一个竞争条件。没有什么能阻止 ThreadB 启动,进入其 运行 方法,并在 ThreadA 进入其同步块之前对其自身进行同步(因此无限期等待)。但是,考虑到新线程开始执行所需的时间,这不太可能发生。
处理此类情况的最简单、最推荐的方法是不编写自己的实现,而是选择使用 Executor 提供的 callable/future。
在没有遵循标准的情况下解决这个特殊情况:
- 在 ThreadB 的同步块末尾设置一个布尔值 'finished'。
- 如果布尔值'finished'在进入synchronized块后为真,那么你不应该调用wait。
是的——这是一场关于哪个线程先进入哪个同步块的竞赛。对于比赛的大多数场景,输出和答案都是相同的。然而,一方面,程序会死锁:
- Main 开始调用 b.start() 并立即安排出去。
- 线程B启动,进入synchronized,调用notify()。
- Main 进入同步块,调用 wait()
在这种情况下,main 将永远等待,因为线程 b 在 main 阻塞在 wait() 上之前调用了 notify。
也就是说,这不太可能 - 但对于所有线程,您应该得出结论,它会发生,然后在最糟糕的时间发生。
对,不能保证哪个线程先执行。线程 b 可以在主线程开始等待之前发出通知。
除此之外,线程可以 return 在没有得到通知的情况下退出等待,因此在技术上设置一个标志并在进入等待之前检查它是不够的。您可以将其重写为
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,则加入它的东西可能 return 提前。这里的一个副作用是,如果您删除通知,代码将以相同的方式工作。
我正在查看一些 notify/wait 示例并发现了这个示例。我知道同步块本质上定义了一个关键部分,但这不是竞争条件吗?没有指定首先进入哪个同步块。
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();
}
}
}
每个网站的输出:
Waiting for b to complete...
Total is: 4950
是的,这是一个竞争条件。没有什么能阻止 ThreadB 启动,进入其 运行 方法,并在 ThreadA 进入其同步块之前对其自身进行同步(因此无限期等待)。但是,考虑到新线程开始执行所需的时间,这不太可能发生。
处理此类情况的最简单、最推荐的方法是不编写自己的实现,而是选择使用 Executor 提供的 callable/future。
在没有遵循标准的情况下解决这个特殊情况:
- 在 ThreadB 的同步块末尾设置一个布尔值 'finished'。
- 如果布尔值'finished'在进入synchronized块后为真,那么你不应该调用wait。
是的——这是一场关于哪个线程先进入哪个同步块的竞赛。对于比赛的大多数场景,输出和答案都是相同的。然而,一方面,程序会死锁:
- Main 开始调用 b.start() 并立即安排出去。
- 线程B启动,进入synchronized,调用notify()。
- Main 进入同步块,调用 wait()
在这种情况下,main 将永远等待,因为线程 b 在 main 阻塞在 wait() 上之前调用了 notify。
也就是说,这不太可能 - 但对于所有线程,您应该得出结论,它会发生,然后在最糟糕的时间发生。
对,不能保证哪个线程先执行。线程 b 可以在主线程开始等待之前发出通知。
除此之外,线程可以 return 在没有得到通知的情况下退出等待,因此在技术上设置一个标志并在进入等待之前检查它是不够的。您可以将其重写为
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,则加入它的东西可能 return 提前。这里的一个副作用是,如果您删除通知,代码将以相同的方式工作。