线程:忙等待 – 空循环

在我们的大学课程中,我们学习了Threads并使用“Busy Waiting”方法作为在TrafficLight等待的CarTrafficLight 。 为此,我们构建了三个类:

  • TrafficLight (implements Runnable)
  • Car (implements Runnable)
  • Main

在我们的Main类中,我们启动两个Thread ,一个是Car ,另一个是TrafficLightCar具有布尔属性hasToWait 。 这个类中的run()方法的工作方式是,只要hasToWait == true ,它就可以通过while循环工作。 要更改此设置,我们在notifyCar()类中使用notifyCar()方法, TrafficLight使用该TrafficLightTrafficLightrun()方法运行Thread.sleep()来模拟一定的等待时间。

我教授的一切都很好,但最终我遇到了严重的问题。 只要Car类中的while循环为空。 当我输入System.out.println() – 它不是空的时,它可以工作。 但是如果Syso为空,则结果是不显示Run of Run方法。 当TrafficLightThread.sleep()0时,它也正常工作。 比它使用空的while循环。

这是我的代码:

Car.java:

 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"); } } 

TrafficLight.java:

 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(); } } 

Main.java:

 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在Eclipse Juno中获得了1:1的代码

除了在另一个答案中说的所有内容(只需用你的hasToWait代替finished该答案),代码在添加println时开始工作的原因如下:

  • println是一种同步方法;
  • 你在两个线程中调用它;
  • 这会在两个线程之间创建一个先发生的关系;
  • 因此,对子线程可以看到对布尔标志的写入。

你可以说它开始大多是偶然的工作:你正在捎带println正在进行的同步。

代码的真正问题是实例字段hasToWait 。 该字段由两个线程使用。 汽车线程读取值,交通灯线程在一段时间后更新该值。

必须以某种方式同步对此字段的访问。

有两种方法可以做到这一点:

  1. 使用synchronized关键字。 通过在所有位置使用同步块,在其中读取或写入,或者 – 更好 – 编写同步的getter和同步的setter,然后使用Car类中的getter和setter。

  2. 使用volatile关键字。 只需声明您的字段为volatile。 这个关键字确实存在于这种情况下。 有关volatile的更多信息可以在Oracle的Java教程中找到 。

在阅读了有关primefaces访问的文章后(参见上面的链接),应该清楚的是,对于这个用例,选项2(声明volatile)是更好的选择。

现在你看到你的计算机和教授的计算机之间的区别:只要你使用的是单核处理器, 你就会看到其他线程中的实例字段的更新,就像它们被同步一样 ,因为CPU没有在其他核心的缓存区域中同步这些值。 如果使用多核处理器,则JVM能够在多个核上运行线程。 这意味着,这些内核必须同步值,并且易失性机制正是为此而设计的。