[英]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
有两种类型, FreeSegment
和Station
。
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.