两个线程如何“在”“同步”方法中

2022-09-03 15:20:45

我真的是Java并发的新手,我正在尝试实现以下规范:

  • 我们有一个停车场,里面有一些停车位
  • 每辆车都表示为一个线程,它无休止地改变汽车状态从驾驶 - 停车。每辆车都有自己的停车位。
  • 当汽车处于停车状态时,它会尝试将车停在某个位置(不需要他的位置)。如果这个地方是免费的,那么它就会停车,否则它将跳过这个停车阶段并返回驾驶。
  • 除非车主想要停车,否则汽车将保持在原地。

这不是确切的规格,但我遇到的唯一问题是以下内容:

我无法让汽车跳过转弯。如果两辆车选择相同的地点,那么一辆停在车上,另一辆等待,直到公园空闲。这不是我想要的巴赫维奥尔。我的第一个想法是简单地将读取和写入同步到占用的变量:

class Car implements Runnable {
    private CarState state = CarState.driving

    run {
        while(true) {
            switch(state) {
            case driving: 
                System.out.println(this + " driving.");
                state = parking;
                break;
            case parking: Spot s = CarPark.getRandomSpot();

                if(s.willingToPark(this)) {
                    System.out.println(s + " occupied. " + this 
                    + " skip park turn.");
                } else {
                    s.park(this);
                } 
                state = driving;
            }
        }

    }
}

class Spot {
    private boolean occupied = false;
    private Car owner = new Car(...);

    synchronized boolean willingToPark(Car c) {
        if(occupied) {
            return true;
        } else {
            occupied = true;
            return false;
    }

    synchronized void park(Car c) {
        System.out.println(c + " parking at " + this);
        //don't care how this is implemented, just keep in mind
        //that it will enter in a loop until the owner came back.
        occupied = false;
    }
}

如果我用三辆车运行这个,那么我最终会让car0停在点1,car1停在点0,car2在点0上等待,因为car1正在执行同步块停车(Car c)。我不明白如果愿意的ToPark是同步的,两辆车怎么可能停在同一个地方。

谢谢


答案 1

我不明白如果愿意的ToPark是同步的,两辆车怎么可能停在同一个地方。

这其实很简单。Car1 捕获 spot0 并开始在循环内部方法中等待所有者(您没有提供代码)。在等待时,它拥有监视器,不允许任何人在 spot0 上调用同步方法。
这就是car2挂在方法上的原因。park()willingToPark()


答案 2

问题在于 的循环。想象一下:park

  • 两个线程都抓住同一个点,.s
  • 他们都试图获取显示器锁定 ;只有一个人会成功。s
  • 成功的一个将导致旋转,同时仍然保持锁定park
  • 另一个耐心地等待,直到锁被释放,以便它可以尝试获取它。

没有什么(除了杀死JVM)可以告诉未能停放的线程停止等待。它将等到它可以获取锁 - 这只会在第一个线程完成循环后发生。spark

解决方案是根本不循环。相反,汽车应该通过一种新的方法取消设置标志。此方法还必须同步,以便跨线程的内存可见性。parkoccupiedunpark(Car)

现在,数据流如下所示:

  • 两个线程都抓住同一个点,.s
  • 他们都试图获取显示器锁定 ;只有一个人会成功。s
  • 成功的那个设置,并立即返回,然后释放锁occupied = trues
  • 另一个获得锁,并看到该点被占用。s

顺便说一句,您甚至不需要为此提供方法。您可以使用 AtomicBoolean 的 compareAndSet 方法,该方法允许您以原子方式检查 AtomicBoolean 的值,并且仅当其当前值是您期望的值时才设置它。因此,表示“原子检查当前值;如果它是假的,则将其设置为 true 并返回 true;如果它是真的,那就保持原样,并返回假的。这种行为很有用,但更高级一些。synchronizedreturn occupied.compareAndSet(false, true)