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

时间:2017-07-17 02:35:50

标签: java multithreading synchronization deadlock monitor

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

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

链表的所有元素都属于Segment类。有两种类型的Segment s,FreeSegmentStation s。

当线程遍历链表时,系统在系统上并发运行。火车线程的代码如下:

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

继续()实施

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

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

进入循环后,某个段的任何列车必须等待,以便下一个段在进行之前清除。这是通过wait()当前段实现的,直到另一个列车线程notify()为止。

然而,经过测试,wait()并未尊重notify() s ,导致列车无缘无故等待,系统陷入僵局

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

其他观察

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

while (nextSegment.isOccupied());

我认为删除wait()会有效,但由于某种原因它仍然会导致死锁。

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

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

它正常工作。

1 个答案:

答案 0 :(得分:2)

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

改为使用Semaphore,其中"允许"代表进入细分受众群的许可,即该细分受众群是"免费"。

当列车想要输入某个细分时,它会调用acquire(),当它离开细分时,会调用release()。所有段都用1个许可证初始化,即所有段最初都是空的"。

您甚至可以使用availablePermits()来确定该细分受众群目前是否已被占用"。

<强>更新

如果您不想使用Semaphore,那么您的代码有什么问题:

您的代码是&#34;锁定&#34;当前段,因此访问该段是受控制的,但以下代码违反了:

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

这里代码访问nextSegment而没有锁定该段,即没有在段上同步。

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

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

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