线程:忙等待 – 空循环
在我们的大学课程中,我们学习了Threads
并使用“Busy Waiting”方法作为在TrafficLight
等待的Car
的TrafficLight
。 为此,我们构建了三个类:
-
TrafficLight (implements Runnable)
-
Car (implements Runnable)
-
Main
在我们的Main
类中,我们启动两个Thread
,一个是Car
,另一个是TrafficLight
。 Car
具有布尔属性hasToWait
。 这个类中的run()
方法的工作方式是,只要hasToWait == true
,它就可以通过while
循环工作。 要更改此设置,我们在notifyCar()
类中使用notifyCar()
方法, TrafficLight
使用该TrafficLight
。 TrafficLight
的run()
方法运行Thread.sleep()
来模拟一定的等待时间。
我教授的一切都很好,但最终我遇到了严重的问题。 只要Car
类中的while
循环为空。 当我输入System.out.println()
– 它不是空的时,它可以工作。 但是如果Syso为空,则结果是不显示Run
of Run
方法。 当TrafficLight
的Thread.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
。 该字段由两个线程使用。 汽车线程读取值,交通灯线程在一段时间后更新该值。
必须以某种方式同步对此字段的访问。
有两种方法可以做到这一点:
-
使用
synchronized
关键字。 通过在所有位置使用同步块,在其中读取或写入,或者 – 更好 – 编写同步的getter和同步的setter,然后使用Car类中的getter和setter。 -
使用
volatile
关键字。 只需声明您的字段为volatile。 这个关键字确实存在于这种情况下。 有关volatile的更多信息可以在Oracle的Java教程中找到 。
在阅读了有关primefaces访问的文章后(参见上面的链接),应该清楚的是,对于这个用例,选项2(声明volatile)是更好的选择。
现在你看到你的计算机和教授的计算机之间的区别:只要你使用的是单核处理器, 你就会看到其他线程中的实例字段的更新,就像它们被同步一样 ,因为CPU没有在其他核心的缓存区域中同步这些值。 如果使用多核处理器,则JVM能够在多个核上运行线程。 这意味着,这些内核必须同步值,并且易失性机制正是为此而设计的。