wait() 没有捕获 notify();导致奇怪的死锁行为

wait() does not catch notify(); causes weird deadlock behavior

我正在为一个火车系统建模,该系统有八个车站,带有线程和监视器。系统建模如下,使用循环链表:

   S2-S3-S4
  /        \
 S1        S5
 >\        /
   S8-S7-S6

链表的所有元素都是classSegmentSegment有两种类型,FreeSegmentStation

作为遍历链表的线程在系统上并发训练运行。 train线程的代码如下:

public void runWithMonitors(boolean showMsgs) {
    // This is the entry monitor
    synchronized (entryPoint) {
        try {
            // Wait until the next segment is clear
            // This loop guards against spurious wakeups as recommended
            // by the official Java documentation
            while (entryPoint.isOccupied()) {
                entryPoint.wait();
            }
        } catch (InterruptedException ex) {
            print("Services interrupted.", showMsgs);
        }
    }

    // Run this code indefinitely
    while (true) {
        // This is the current segment monitor
        // Only one train can ever occupy this segment
        // Take note of the current segment
        Segment currSegmentMonitor = currSegment;

        synchronized (currSegmentMonitor) {
            // Take this spot
            currSegment.setIsOccupied(true);
            currSegment.setTrainInside(this);

            // If this segment is a station, load and unload passengers
            if (currSegmentMonitor instanceof Station) {
                // Open doors and allow passengers to get off and on
                alightPassengers(showMsgs);
                loadPassengers(showMsgs);
            }

            // Notify this train's observer that its position has changed
            trainObserver.update(dispatcher, showMsgs);

            // Is it okay to proceed?
            try {
                // Wait until the next segment is clear
                // This loop guards against spurious wakeups as recommended
                // by the official Java documentation
                while (nextSegment.isOccupied()) {
                    currSegmentMonitor.wait();
                }
            } catch (InterruptedException ex) {
                print("Services interrupted.", showMsgs);
            }

            // Leave this spot
            currSegment.setIsOccupied(false);
            currSegment.setTrainInside(null);

            // If ready, then proceed
            proceed();

            // Then tell others we're done occupying that spot
            currSegmentMonitor.notify();
        }
    }
}

proceed() 实现

// Move forward
private void proceed() {
    // We've just moved to the next segment
    currSegment = nextSegment;
    nextSegment = currSegment.getNextSegment();
}

列车进入系统前,必须等待进入段清空。入口段由第一个站点 (S1) 之前的 > 字符表示。

一旦进入环路,任何在一个路段的列车都必须等待等待下一个路段通行后再继续。这是通过 wait() 在当前段上进行直到另一个火车线程 notify() 来实现的。

然而,在测试中,wait()s 根本不尊重 notify()s ,导致火车无缘无故地等待,系统死锁。

我正在用两个或更多线程测试系统。

补充观察

我尝试用代码 wait() 替换 try 块:

while (nextSegment.isOccupied());

我假设删除 wait() 会起作用,但由于某种原因它仍然会导致死锁。

有趣的是,当在繁忙的等待中放置调试打印语句时,如下所示:

while (nextSegment.isOccupied()) {
    System.out.println("Next segment: " +   nextSegment.isOccupied());
}

正常工作

不要使用监视器。监视器的问题是,如果没有线程在等待,notify() 调用将被忽略。

改用Semaphore,其中"permit"代表进入段的权限,即段是"free".

当火车要进入一个区段时,它会调用acquire(), and when it leaves a segment, it calls release()。所有段都使用 1 个许可进行初始化,即所有段最初都是 "empty".

您甚至可以使用 availablePermits() 来确定该段当前是否为 "occupied"。


更新

如果您不想使用 Semaphore,您的代码有以下问题:

您的代码是 "locking" 当前段,因此对该段的访问受到控制,但是以下代码违反了这一点:

while (nextSegment.isOccupied()) {
    currSegmentMonitor.wait();
}

这里的代码访问 nextSegment 时没有锁定该段,即没有同步该段。

除此之外,代码在错误的监视器上等待,因为它在当前监视器上等待,即使它应该在下一个监视器上等待。

将代码更改为此,以修复它:

synchronized (nextSegment) {
    while (nextSegment.isOccupied()) {
        nextSegment.wait();
    }
}