在我们上大学的课程中,我们了解了Threads
并使用“忙碌等待”方法获取Car
等待TrafficLight
的示例。对于此任务,我们构建了三个类:
TrafficLight (implements Runnable)
Car (implements Runnable)
Main
在我们的Main
课程中,我们开始了两个Thread
,一个是Car
,另一个是TrafficLight
。 Car
具有布尔属性hasToWait
。此类中的run()
方法的工作方式是,只要while
,它就可以通过hasToWait == true
循环工作。要更改此设置,我们在notifyCar()
类中使用了Car
方法,该方法由TrafficLight
使用。 run()
中的TrafficLight
方法会运行Thread.sleep()
来模拟等待的特定时间。
我教授的一切都很好,但最终我遇到了严重的问题。只要while
类中的Car
循环为空。当我输入System.out.println()
- 不为空时,它就可以了。但是如果Syso为空,则结果是不显示Run
方法的文本。
当Thread.sleep()
中的TrafficLight
为0
时,它也会正常工作。比它使用空while
循环。
这是我的代码:
package trafficlight;
public class Car implements Runnable {
private boolean hasToWait = true;
public void run() {
this.crossTrafficLight();
}
public void crossTrafficLight() {
while(hasToWait){ for(int i = 0; i<20; i++){System.out.println("123");}} // Busy waiting
System.out.println("Auto fährt über Ampel");
}
public void notifyCar() {
this.hasToWait = false;
System.out.println("Test");
}
}
package trafficlight;
public class TrafficLight implements Runnable {
private Car car;
public TrafficLight(Car car) {
this.car = car;
}
@Override
public void run() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.car.notifyCar();
}
}
package trafficlight;
public class Main {
public static void main(String[] args){
Car car = new Car();
TrafficLight tl = new TrafficLight(car);
new Thread(car).start();
new Thread(tl).start();
}
}
问题出在哪里?为什么它适用于我的教授而不是我的电脑?我使用JRE 1.7
答案 0 :(得分:3)
除了this other answer中所说的所有内容(在该答案中只替换hasToWait
代表finished
),代码在添加println
时开始工作的原因如下:
println
是 synchronized 方法; 你可以说它主要是开始工作:你正在捎带println
中正在进行的同步。
答案 1 :(得分:1)
代码的真正问题是实例字段hasToWait
。该字段由两个线程使用。汽车线程读取值,交通灯线程在一段时间后更新该值。
必须以某种方式同步对此字段的访问。
有两种方法可以做到这一点:
使用synchronized
关键字。通过在所有位置使用同步块,在其中读取或写入,或者 - 更好 - 编写同步的getter和同步的setter,然后使用Car类中的getter和setter。
使用volatile
关键字。只需声明您的字段为volatile。这个关键字确实存在于这种情况下。有关volatile的更多信息,请参阅Oracle's Java Tutorials。
在阅读了有关原子访问的文章后(参见上面的链接),应该清楚的是选项2(声明volatile)是更好的选择 - 对于这个用例。
现在您的计算机和教授的计算机之间存在差异:只要您使用的是单核处理器,就会看到其他线程中的实例字段的更新,就像它们已经同步一样< / em>,因为CPU不必在其他内核的缓存区域中同步这些值。如果使用多核处理器,则JVM能够在多个核上运行线程。这意味着,这些内核必须同步值,并且易失性机制正是为此而设计的。