wait() 没有捕获 notify();导致奇怪的死锁行为
wait() does not catch notify(); causes weird deadlock behavior
我正在为一个火车系统建模,该系统有八个车站,带有线程和监视器。系统建模如下,使用循环链表:
S2-S3-S4
/ \
S1 S5
>\ /
S8-S7-S6
链表的所有元素都是classSegment
。 Segment
有两种类型,FreeSegment
和Station
。
作为遍历链表的线程在系统上并发训练运行。 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();
}
}
我正在为一个火车系统建模,该系统有八个车站,带有线程和监视器。系统建模如下,使用循环链表:
S2-S3-S4
/ \
S1 S5
>\ /
S8-S7-S6
链表的所有元素都是classSegment
。 Segment
有两种类型,FreeSegment
和Station
。
作为遍历链表的线程在系统上并发训练运行。 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();
}
}