5

在大学的课程中,我们学习Threads并使用了“忙着等待”的方法来举例说明CarTrafficLight. 对于这个任务,我们构建了三个类:

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

在我们的Main班级中,我们开始两个Threads,一个Car,和一个TrafficLight。具有Car布尔属性hasToWait。此类中的run()方法的工作方式是,while只要hasToWait == true. 为了改变这一点,我们notifyCar()在类中有一个方法,CarTrafficLight. 中的run()方法TrafficLight运行一个Thread.sleep()来模拟一定的等待时间。

在我的教授那里一切正常,但最终我遇到了严重的问题。只要类中的while循环Car为空。当我输入一个空的System.out.println()-时,它可以工作。但如果 Syso 为空,则结果是不显示该方法的 Text。当in为时它也可以工作。比它适用于空循环。RunThread.sleep()TrafficLight0while

这是我的代码:

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

交通灯.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();
    }
}

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

}

问题出在哪里?为什么它在我的教授上有效,但在我的电脑上无效?我在我的 Eclipse Juno 中得到了 1:1 的代码,使用JRE 1.7

4

2 回答 2

5

除了这个其他答案中所说的一切(只需在那个答案中替换你的hasToWaitfor finished),添加 a 时代码开始工作的原因println如下:

  • println是一种同步方法;
  • 你在两个线程中调用它;
  • 这在两个线程之间创建了一个happens-before关系;
  • 因此,对布尔标志的写入对子线程可见。

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

于 2014-01-23T11:17:22.400 回答
1

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

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

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

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

  2. 使用volatile关键字。只需将您的字段声明为 volatile。该关键字正是针对这种情况而存在的。有关 volatile 的更多信息,请参见Oracle 的 Java 教程

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

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

于 2014-01-23T13:05:19.927 回答