简体   繁体   English

wait()不能捕获notify(); 导致怪异的死锁行为

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

I'm modelling a train system with eight stations with threads and monitors. 我正在为一个火车系统建模,该系统具有八个带有线程和监视器的站点。 The system is modeled as follows, using a circular linked list: 使用循环链表对系统进行如下建模:

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

All elements of the linked list are of the class Segment . 链表的所有元素都属于Segment类。 There are two types of Segment s, FreeSegment s and Station s. Segment有两种类型, FreeSegmentStation

Trains run concurrently on the system as threads traversing the linked list. 当线程遍历链表时,火车在系统上同时运行。 The code of the train thread is as follows: 训练线程的代码如下:

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() implementation 进行procedure()实现

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

Before a train can enter the system, it must wait for the entry segment to be clear. 在火车可以进入系统之前,它必须等待进入路段被清除。 The entry segment is denoted by the > character before the first station (S1). 入口段在第一个站之前用>字符表示(S1)。

Once inside the loop, any train at a segment must wait for the next segment to be clear before it proceeds. 一旦进入环路,任何路段的列车都必须等待下一个路段被清除才能继续。 This is implemented by wait() ing on the current segment until another train thread notify() s it. 这是通过在当前段上执行wait()直到另一个火车线程notify()的。

However, upon testing, the wait() s don't honor the notify() s at all , causing a train to wait for no reason, deadlocking the system. 然而,在测试中, wait() ■不要兑现notify() s 的所有 ,造成一列火车,等待没有任何理由,死锁系统。

I'm testing the system with two or more threads. 我正在用两个或多个线程测试系统。

Additional observations 其他观察

I tried to replace the try block with the wait() with the code: 我试图用代码用wait()替换try块:

while (nextSegment.isOccupied());

I assumed that removing the wait() would work, but it still results in deadlocks for some reason. 我以为删除wait()会起作用,但是由于某种原因它仍然会导致死锁。

The interesting part, though, is that when placing a debug print statement inside the busy wait, like so: 但是,有趣的部分是,在繁忙的等待中放置调试打印语句时,如下所示:

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

It works normally. 它正常工作。

Don't use monitors. 不要使用显示器。 The problem with monitors is that if no threads are waiting, the notify() call is ignored. 监视器的问题是,如果没有线程在等待,则notify()调用将被忽略。

Use Semaphore instead, where the "permit" represents the permission to enter the segment, ie that the segment is "free". 请改用Semaphore ,其中“ permit”表示进入该段的权限,即该段是“ free”。

When a train wants to enter a segment, it calls acquire() , and when it leaves a segment, it calls release() . 火车想要进入路段时,它会调用acquire() ,而离开路段时,它会调用release() All the segments are initialized with 1 permit, ie all segments are initially "empty". 所有段都以1个许可进行初始化,即所有段最初都是“空”的。

You can even use the availablePermits() to determine if the segment is currently "occupied". 您甚至可以使用availablePermits()来确定该段当前是否被“占用”。


UPDATE UPDATE

If you don't want to use Semaphore , here's what wrong with your code: 如果您不想使用Semaphore ,那么您的代码Semaphore问题:

Your code is "locking" the current segment, so access to the segment is controlled, however the following code violates that: 您的代码“锁定”了当前段,因此可以控制对该段的访问,但是以下代码违反了这一点:

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

Here the code accesses the nextSegment without having a lock on that segment, ie without synchronizing on the segment. 在这里,代码访问nextSegment无需锁定该段,即无需在该段上进行同步。

In addition to that, the code is waiting on the wrong monitor, because it is waiting on the current monitor, even though it should be waiting on the next monitor. 除此之外,代码正在错误的监视器上等待,因为它正在当前的监视器上等待,即使它应该在下一个监视器上也正在等待。

Change code to this, to fix it: 更改代码以解决此问题:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM