我在Java中有一个代码,其中两个对象在完成一个处理后等待并相互通知。我将使用以下示例保持我的代码简单,并假设没有语法错误(我只是想让您知道逻辑在这里更重要而不是语法)。
假设我有对象A,它是具有此伪代码的线程
class A is Thread {
run() {
while(true) {
wait(); // wait for signal from B
// then do something if signal received
B.signal(); // let B know that we're done and wait again
}
}
}
然后我们这里有B,它也是一个有这个伪代码的线程
class B is Thread {
run() {
while(true) {
// Do something
A.signal(); // Let A know to continue processing
wait(); // Wait for signal from A before doing something again
}
}
}
所以你可以看到那里有一个周期。问题是我有一个死锁,这里的原因是因为当A完成处理时,它会在B等待之前发信号通知B.但是在通知B的时候,A仍有可能出现问题。到达了wait()代码,B已经调用了A.signal()并导致死锁。
如何正确解决此问题?我想到的解决方案是,当B被通知工作时,我会让B的线程休眠几毫秒,但我不认为这是一个好主意。感谢任何帮助,提前谢谢。
答案 0 :(得分:1)
当您使用notify()时,这应该与状态更改相关联。
使用wait()时,应该检查状态更改。
在实际代码中,您只能在等待某事时等待。
注意:wait()可以虚假唤醒,并不意味着调用了notify()。正如您所注意到的,如果没有等待(),则notify()不执行任何操作。
您可以使用BlockingQueue在线程之间传递工作/消息,而不是使用此模式。这有wait / notify和包含内置工作的对象。
但是,由于您通常需要一个线程来完成工作,因此内置了一个ExecutorService来执行此操作。这允许您将工作传递给线程池并收集结果。
简而言之,您应该使用ExecutorService。
答案 1 :(得分:1)
如果A使用B的结果,那么您可以考虑使用BlockingQueue。
答案 2 :(得分:0)
正如您在the Javadoc中所述,您需要将wait
调用放在检查条件的循环中。否则,如果您没有可以检查的条件变量或表达式,那么您可能会错过通知,因为您没有在此等待。
此外,正如其他人所指出的那样,您需要按住正在调用wait
或notify
方法的对象的监视器;这是synchronized
关键字的用途。
在下面的修复中,条件非常简单;它是A和B类中名为notified
的变量。
另外,为了做到这一点,A和B需要彼此了解。在你的代码中,你似乎在调用静态方法;但是需要在实例上调用notify
方法,因此需要分别在B和A中保留对A和B实例的引用。
这解决了问题:
class A is Thread {
private B b;
private boolean notified;
public void run() {
while(true) {
synchronized(this) {
while (!notified) {
try {
wait(); // wait for signal from B
} catch (InterruptedException e) {}
}
notified = false;
}
synchronized(b) {
// then do something if signal received
b.notified = true;
b.notify(); // let B know that we're done and wait again
}
}
}
}
class B is Thread {
private A a;
private boolean notified;
public void run() {
while(true) {
synchronized(a) {
// Do something
a.notified = true;
a.notify(); // Let A know to continue processing
}
synchronized(this) {
while (!notified) {
try {
wait(); // Wait for signal from A before doing something again
} catch (InterruptedException e) {}
}
notified = false;
}
}
}
}